#include "key_nav.h" #include "button.h" #include "sleep_mgr.h" #include "pages.h" #include "esp_log.h" #include "esp_lvgl_port.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "../ui/ui.h" #include "../ui/screens/ui_ScreenSet.h" #include "../ui/screens/ui_ScreenImg.h" static const char *TAG = "KEY_NAV"; // 当前导航上下文 static nav_context_t current_ctx = NAV_CTX_HOME; // Set界面焦点状态 static set_focus_item_t set_focus = SET_FOCUS_NONE; // Set界面可聚焦的LVGL对象(在Set界面init后有效) static lv_obj_t *set_focus_objects[SET_FOCUS_COUNT] = {NULL}; // 蓝色边框样式常量 #define FOCUS_BORDER_COLOR 0x2196F3 // Material Blue #define FOCUS_BORDER_WIDTH 3 // ==================== Set界面焦点管理 ==================== // 更新焦点对象指针(Set界面初始化后调用) static void update_focus_objects(void) { set_focus_objects[SET_FOCUS_LOW_POWER] = ui_ImgLowPower; set_focus_objects[SET_FOCUS_FLASHLIGHT] = ui_ImgFlashlight; set_focus_objects[SET_FOCUS_DELETE] = ui_ImgDelete; set_focus_objects[SET_FOCUS_BRIGHTNESS] = ui_SliderBrightness; } // 清除所有焦点边框 static void clear_all_focus_borders(void) { if (!lvgl_port_lock(50)) return; for (int i = 0; i < SET_FOCUS_COUNT; i++) { if (set_focus_objects[i]) { lv_obj_set_style_border_width(set_focus_objects[i], 0, LV_PART_MAIN | LV_STATE_DEFAULT); } } lvgl_port_unlock(); } // 设置指定对象的焦点边框 static void set_focus_border(int index) { if (index < 0 || index >= SET_FOCUS_COUNT) return; if (!set_focus_objects[index]) return; if (!lvgl_port_lock(50)) return; lv_obj_set_style_border_color(set_focus_objects[index], lv_color_hex(FOCUS_BORDER_COLOR), LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_border_width(set_focus_objects[index], FOCUS_BORDER_WIDTH, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_border_opa(set_focus_objects[index], LV_OPA_COVER, LV_PART_MAIN | LV_STATE_DEFAULT); lvgl_port_unlock(); } // Set焦点移动到下一个 static void set_focus_next(void) { update_focus_objects(); clear_all_focus_borders(); // 从当前焦点移到下一个(循环:NONE→0→1→2→3→NONE) set_focus++; if (set_focus >= SET_FOCUS_COUNT) { set_focus = SET_FOCUS_NONE; ESP_LOGI(TAG, "Set焦点:无选中"); return; } set_focus_border(set_focus); ESP_LOGI(TAG, "Set焦点:%d", set_focus); } // ==================== 全局返回Home ==================== // 派发到独立任务执行(iot_button回调在esp_timer中,不能vTaskDelay) typedef struct { int gpio_num; btn_event_type_t event; } nav_evt_t; static void nav_task_go_home(void *arg) { ESP_LOGI(TAG, "双击BOOT:返回Home"); // 如果手电筒活跃,先退出 if (flashlight_is_active()) { uint8_t saved_br = flashlight_get_saved_brightness(); flashlight_exit(); vTaskDelay(pdMS_TO_TICKS(80)); // 切换到Home if (lvgl_port_lock(100)) { _ui_screen_change(&ui_ScreenHome, LV_SCR_LOAD_ANIM_NONE, 0, 0, &ui_ScreenHome_screen_init); lvgl_port_unlock(); } vTaskDelay(pdMS_TO_TICKS(150)); pwm_set_brightness(saved_br); } else { // 隐藏Img删除容器(如果在Img界面) if (lvgl_port_lock(100)) { lv_obj_t *scr = lv_scr_act(); if (scr == ui_ScreenImg) { ui_ScreenImg_hide_delete_container(); } _ui_screen_change(&ui_ScreenHome, LV_SCR_LOAD_ANIM_NONE, 0, 0, &ui_ScreenHome_screen_init); lvgl_port_unlock(); } } current_ctx = NAV_CTX_HOME; set_focus = SET_FOCUS_NONE; vTaskDelete(NULL); } // ==================== Home界面按键 ==================== static void nav_task_home_boot_click(void *arg) { // BOOT单击:切换到Set ESP_LOGI(TAG, "Home: BOOT单击→Set"); if (lvgl_port_lock(100)) { ui_ScreenSet_set_previous(&ui_ScreenHome, &ui_ScreenHome_screen_init); _ui_screen_change(&ui_ScreenSet, LV_SCR_LOAD_ANIM_NONE, 0, 0, &ui_ScreenSet_screen_init); lvgl_port_unlock(); } current_ctx = NAV_CTX_SET; set_focus = SET_FOCUS_NONE; vTaskDelete(NULL); } static void nav_task_home_key_click(void *arg) { // KEY单击:切换到Img ESP_LOGI(TAG, "Home: KEY单击→Img"); if (lvgl_port_lock(100)) { _ui_screen_change(&ui_ScreenImg, LV_SCR_LOAD_ANIM_NONE, 0, 0, &ui_ScreenImg_screen_init); lvgl_port_unlock(); } current_ctx = NAV_CTX_IMG; vTaskDelete(NULL); } // ==================== Img界面按键 ==================== static void nav_task_img_key_click(void *arg) { if (current_ctx == NAV_CTX_IMG_DELETE) { // 删除模式:KEY单击→取消删除 ESP_LOGI(TAG, "Img删除模式: KEY单击→取消"); if (lvgl_port_lock(100)) { ui_ScreenImg_hide_delete_container(); lvgl_port_unlock(); } current_ctx = NAV_CTX_IMG; } else { // 正常浏览:KEY单击→下一张图片 ESP_LOGI(TAG, "Img: KEY单击→下一张"); const char *next = get_next_image(); if (next) { if (lvgl_port_lock(100)) { update_ui_ImgBle(next); lvgl_port_unlock(); } } } vTaskDelete(NULL); } static void nav_task_img_boot_click(void *arg) { if (current_ctx == NAV_CTX_IMG_DELETE) { // 删除模式:BOOT单击→确认删除 ESP_LOGI(TAG, "Img删除模式: BOOT单击→删除"); if (lvgl_port_lock(100)) { if (delete_current_image()) { const char *next = get_current_image(); if (next) { update_ui_ImgBle(next); } } // 删除后隐藏容器 ui_ScreenImg_hide_delete_container(); lvgl_port_unlock(); } current_ctx = NAV_CTX_IMG; } else { // 正常浏览:BOOT单击→返回Home ESP_LOGI(TAG, "Img: BOOT单击→Home"); if (lvgl_port_lock(100)) { ui_ScreenImg_hide_delete_container(); _ui_screen_change(&ui_ScreenHome, LV_SCR_LOAD_ANIM_NONE, 0, 0, &ui_ScreenHome_screen_init); lvgl_port_unlock(); } current_ctx = NAV_CTX_HOME; } vTaskDelete(NULL); } // ==================== Set界面按键 ==================== static void nav_task_set_key_click(void *arg) { if (current_ctx == NAV_CTX_SET_BRIGHTNESS) { // 亮度调节模式:KEY单击→亮度-10% uint8_t br = pwm_get_brightness(); if (br > 10) br -= 10; else br = 10; pwm_set_brightness(br); // 同步更新UI if (lvgl_port_lock(100)) { if (ui_SliderBrightness) { lv_slider_set_value(ui_SliderBrightness, br, LV_ANIM_OFF); } if (ui_LabelBrightness) { char buf[8]; snprintf(buf, sizeof(buf), "%d%%", br); lv_label_set_text(ui_LabelBrightness, buf); } lvgl_port_unlock(); } ESP_LOGI(TAG, "亮度-10%%: %d%%", br); } else { // 焦点导航:KEY单击→下一个图标 set_focus_next(); } vTaskDelete(NULL); } static void nav_task_set_boot_click(void *arg) { if (current_ctx == NAV_CTX_SET_BRIGHTNESS) { // 亮度调节模式:BOOT单击→亮度+10% uint8_t br = pwm_get_brightness(); if (br < 100) br += 10; else br = 100; pwm_set_brightness(br); if (lvgl_port_lock(100)) { if (ui_SliderBrightness) { lv_slider_set_value(ui_SliderBrightness, br, LV_ANIM_OFF); } if (ui_LabelBrightness) { char buf[8]; snprintf(buf, sizeof(buf), "%d%%", br); lv_label_set_text(ui_LabelBrightness, buf); } lvgl_port_unlock(); } ESP_LOGI(TAG, "亮度+10%%: %d%%", br); } else if (set_focus == SET_FOCUS_NONE) { // 无焦点:BOOT单击→返回前一界面 ESP_LOGI(TAG, "Set: BOOT单击→返回前一界面"); clear_all_focus_borders(); if (lvgl_port_lock(100)) { // 尝试返回前一界面,默认Home _ui_screen_change(&ui_ScreenHome, LV_SCR_LOAD_ANIM_NONE, 0, 0, &ui_ScreenHome_screen_init); lvgl_port_unlock(); } current_ctx = NAV_CTX_HOME; set_focus = SET_FOCUS_NONE; } else { // 有焦点:BOOT单击→激活选中功能 ESP_LOGI(TAG, "Set: BOOT激活焦点 %d", set_focus); switch (set_focus) { case SET_FOCUS_LOW_POWER: { // 切换节能模式 bool enabled = sleep_mgr_is_enabled(); sleep_mgr_set_enabled(!enabled); // 更新imgbtn的checked状态 if (lvgl_port_lock(100)) { if (ui_ImgLowPower) { if (!enabled) { lv_obj_add_state(ui_ImgLowPower, LV_STATE_CHECKED); } else { lv_obj_clear_state(ui_ImgLowPower, LV_STATE_CHECKED); } // 强制重绘,确保imgbtn图片立即切换 lv_obj_invalidate(ui_ImgLowPower); } lvgl_port_unlock(); } ESP_LOGI(TAG, "节能模式: %s", !enabled ? "开启" : "关闭"); break; } case SET_FOCUS_FLASHLIGHT: { // 进入应援灯模式 ESP_LOGI(TAG, "进入应援灯模式"); if (lvgl_port_lock(100)) { clear_all_focus_borders(); lvgl_port_unlock(); } // show_flashlight是static的,需要通过模拟点击触发 // 直接调用外部接口 if (lvgl_port_lock(100)) { lv_event_send(ui_ImgFlashlight, LV_EVENT_CLICKED, NULL); lvgl_port_unlock(); } current_ctx = NAV_CTX_FLASHLIGHT; break; } case SET_FOCUS_DELETE: { // 跳转Img删除模式 ESP_LOGI(TAG, "进入删除模式"); clear_all_focus_borders(); if (lvgl_port_lock(100)) { _ui_screen_change(&ui_ScreenImg, LV_SCR_LOAD_ANIM_NONE, 0, 0, &ui_ScreenImg_screen_init); ui_ScreenImg_show_delete_container(); // 高亮确认删除图标(提示用户BOOT=删除) if (ui_ImageDel) { lv_obj_set_style_border_color(ui_ImageDel, lv_color_hex(FOCUS_BORDER_COLOR), LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_border_width(ui_ImageDel, FOCUS_BORDER_WIDTH, LV_PART_MAIN | LV_STATE_DEFAULT); lv_obj_set_style_border_opa(ui_ImageDel, LV_OPA_COVER, LV_PART_MAIN | LV_STATE_DEFAULT); } lvgl_port_unlock(); } current_ctx = NAV_CTX_IMG_DELETE; set_focus = SET_FOCUS_NONE; break; } case SET_FOCUS_BRIGHTNESS: { // 进入亮度调节模式并直接增加10%(无需二次按键激活) current_ctx = NAV_CTX_SET_BRIGHTNESS; uint8_t br = pwm_get_brightness(); if (br <= 90) br += 10; else br = 100; pwm_set_brightness(br); if (lvgl_port_lock(100)) { if (ui_SliderBrightness) { lv_slider_set_value(ui_SliderBrightness, br, LV_ANIM_OFF); } if (ui_LabelBrightness) { char buf[8]; snprintf(buf, sizeof(buf), "%d%%", br); lv_label_set_text(ui_LabelBrightness, buf); } lvgl_port_unlock(); } ESP_LOGI(TAG, "进入亮度调节+10%%: %d%%", br); break; } default: break; } } vTaskDelete(NULL); } // Set界面KEY长按:退出亮度调节模式 static void nav_task_set_key_long(void *arg) { if (current_ctx == NAV_CTX_SET_BRIGHTNESS) { ESP_LOGI(TAG, "长按KEY:退出亮度调节"); current_ctx = NAV_CTX_SET; // 焦点移到下一个(循环回无选中) set_focus_next(); } vTaskDelete(NULL); } // ==================== 应援灯模式按键 ==================== static void nav_task_flashlight_boot_click(void *arg) { // BOOT单击:切换颜色 // flashlight_switch_color 使用 DISPOFF→直接写GRAM→DISPON,无需操作PWM ESP_LOGI(TAG, "应援灯: BOOT单击→切换颜色"); flashlight_switch_color(); // DISPOFF/DISPON已处理显示切换,只需重启闪烁 flashlight_restart_blink(); vTaskDelete(NULL); } static void nav_task_flashlight_key_click(void *arg) { // KEY单击:退出应援灯,返回Set ESP_LOGI(TAG, "应援灯: KEY单击→退出到Set"); uint8_t saved_br = flashlight_get_saved_brightness(); flashlight_exit(); vTaskDelay(pdMS_TO_TICKS(80)); if (lvgl_port_lock(100)) { // 不切换界面,Set界面还在下面 lvgl_port_unlock(); } vTaskDelay(pdMS_TO_TICKS(100)); pwm_set_brightness(saved_br); current_ctx = NAV_CTX_SET; set_focus = SET_FOCUS_NONE; vTaskDelete(NULL); } // ==================== 按键事件总调度 ==================== // 创建任务执行按键处理(避免在esp_timer上下文中vTaskDelay) #define NAV_TASK_STACK 3072 static void dispatch_task(TaskFunction_t func, const char *name) { xTaskCreate(func, name, NAV_TASK_STACK, NULL, 5, NULL); } // BOOT按键事件处理 static void boot_event_handler(int gpio_num, btn_event_type_t event, void *usr_data) { // 先处理屏幕唤醒 if (sleep_mgr_is_screen_off()) { sleep_mgr_notify_activity(); return; // 仅唤醒,不触发业务 } // 通知活动(重置休眠计时器) sleep_mgr_notify_activity(); // 双击BOOT:全局返回Home if (event == BTN_EVT_DOUBLE_CLICK) { dispatch_task(nav_task_go_home, "go_home"); return; } // 单击BOOT:根据当前上下文分发 if (event == BTN_EVT_CLICK) { switch (current_ctx) { case NAV_CTX_HOME: dispatch_task(nav_task_home_boot_click, "h_boot"); break; case NAV_CTX_IMG: case NAV_CTX_IMG_DELETE: dispatch_task(nav_task_img_boot_click, "i_boot"); break; case NAV_CTX_SET: case NAV_CTX_SET_BRIGHTNESS: dispatch_task(nav_task_set_boot_click, "s_boot"); break; case NAV_CTX_FLASHLIGHT: dispatch_task(nav_task_flashlight_boot_click, "f_boot"); break; } } } // KEY2按键事件处理 static void key2_event_handler(int gpio_num, btn_event_type_t event, void *usr_data) { // 先处理屏幕唤醒 if (sleep_mgr_is_screen_off()) { sleep_mgr_notify_activity(); return; } sleep_mgr_notify_activity(); if (event == BTN_EVT_CLICK) { switch (current_ctx) { case NAV_CTX_HOME: dispatch_task(nav_task_home_key_click, "h_key"); break; case NAV_CTX_IMG: case NAV_CTX_IMG_DELETE: dispatch_task(nav_task_img_key_click, "i_key"); break; case NAV_CTX_SET: case NAV_CTX_SET_BRIGHTNESS: dispatch_task(nav_task_set_key_click, "s_key"); break; case NAV_CTX_FLASHLIGHT: dispatch_task(nav_task_flashlight_key_click, "f_key"); break; } } else if (event == BTN_EVT_LONG_PRESS) { switch (current_ctx) { case NAV_CTX_SET_BRIGHTNESS: dispatch_task(nav_task_set_key_long, "s_klong"); break; default: break; } } } // ==================== 初始化 ==================== void key_nav_init(void) { current_ctx = NAV_CTX_HOME; set_focus = SET_FOCUS_NONE; button_on_boot_event(boot_event_handler, NULL); button_on_key2_event(key2_event_handler, NULL); ESP_LOGI(TAG, "按键导航管理器初始化完成"); } nav_context_t key_nav_get_context(void) { return current_ctx; } void key_nav_set_context(nav_context_t ctx) { current_ctx = ctx; } set_focus_item_t key_nav_get_set_focus(void) { return set_focus; } void key_nav_reset_set_focus(void) { clear_all_focus_borders(); set_focus = SET_FOCUS_NONE; }