/* * Copyright 2026 NXP * NXP Proprietary. This software is owned or controlled by NXP and may only be used strictly in * accordance with the applicable license terms. By expressly accepting such terms or by downloading, installing, * activating and/or otherwise using the software, you are agreeing that you have read, and that you agree to * comply with and are bound by, such license terms. If you do not agree to be bound by the applicable license * terms, then you may not retain, install, activate or otherwise use the software. */ #include "events_init.h" #include #include #include "core/lv_obj_style_gen.h" #include "lvgl.h" #include "ui.h" #include "ui_nav.h" #include "UI_DISPATCH/ui_dispatch.h" #include "esp_system.h" #include "esp_sleep.h" #include "driver/rtc_io.h" #include "config.h" #include "widgets_init.h" #include "remapping.h" #include "custom.h" #include "SCREEN/screen_brightness.h" #include "SCREEN/screen_sleep.h" #include "BLE/ble_send.h" static const char *TAG_SLEEP_EVT = "sleep_evt"; extern lv_obj_t *ui_Screen_Lottie; // 【解决方案二】正念练习活跃状态标志(用于禁用手势切换) volatile bool g_mindfulness_active = false; // 【性能优化】手势防抖机制:防止快速滑动时触发多次切换 static uint32_t last_gesture_time = 0; #define GESTURE_DEBOUNCE_MS 300 // 300ms防抖间隔 static bool is_gesture_allowed(void) { uint32_t now = lv_tick_get(); if (now - last_gesture_time < GESTURE_DEBOUNCE_MS) { return false; // 忽略过快的手势 } last_gesture_time = now; return true; } #if LV_USE_GUIDER_SIMULATOR && LV_USE_FREEMASTER #include "freemaster_client.h" #endif //包含点击事件:休眠】【产品说明】【使用说明】【恢复出厂设置】【亮度调节】 static void screen_sortition_btn_history_event_handler(lv_event_t *e); static void sortition_text_back_to_components(lv_event_t *e); static void screen_home_weather_event_handler (lv_event_t *e) { lv_event_code_t code = lv_event_get_code(e); switch (code) { case LV_EVENT_GESTURE: { if (!is_gesture_allowed()) return; // 防抖检查 lv_dir_t dir = lv_indev_get_gesture_dir(lv_indev_active()); switch(dir) { case LV_DIR_LEFT: { lv_indev_wait_release(lv_indev_active()); ui_nav_load_scr_animation(&guider_ui, &guider_ui.screen_time, guider_ui.screen_time_del, &guider_ui.screen_home_weather_del, setup_scr_screen_time, LV_SCR_LOAD_ANIM_MOVE_LEFT, 150, 0, false, true, 2); break; } case LV_DIR_RIGHT: { lv_indev_wait_release(lv_indev_active()); ui_nav_load_scr_animation(&guider_ui, &guider_ui.screen_component, guider_ui.screen_component_del, &guider_ui.screen_home_weather_del, setup_scr_screen_component, LV_SCR_LOAD_ANIM_MOVE_RIGHT, 150, 0, false, true, UI_IDX_COMPONENTS); break; } case LV_DIR_BOTTOM: { lv_indev_wait_release(lv_indev_active()); ui_nav_load_scr_animation(&guider_ui, &guider_ui.screen_pull_down, guider_ui.screen_pull_down_del, &guider_ui.screen_home_weather_del, setup_scr_screen_pull_down, LV_SCR_LOAD_ANIM_OVER_BOTTOM, 150, 0, false, true, UI_IDX_PULLDOWN); break; } default: break; } break; } default: break; } } void events_init_screen_home_weather (lv_ui *ui) { lv_obj_add_event_cb(ui->screen_home_weather, ui_event_gesture_cb, LV_EVENT_ALL, ui); } void events_init_screen_time (lv_ui *ui) { lv_obj_add_event_cb(ui->screen_time, ui_event_gesture_cb, LV_EVENT_ALL, ui); } static void screen_pull_down_event_handler (lv_event_t *e) { lv_event_code_t code = lv_event_get_code(e); switch (code) { case LV_EVENT_GESTURE: { if (!is_gesture_allowed()) return; // 防抖检查 lv_dir_t dir = lv_indev_get_gesture_dir(lv_indev_active()); switch(dir) { case LV_DIR_TOP: { lv_indev_wait_release(lv_indev_active()); ui_nav_load_scr_animation(&guider_ui, &guider_ui.screen_home_weather, guider_ui.screen_home_weather_del, &guider_ui.screen_pull_down_del, setup_scr_screen_home_weather, LV_SCR_LOAD_ANIM_MOVE_TOP, 150, 0, false, true, 1); break; } default: break; } break; } default: break; } } static void screen_pull_down_btn_Wifi_event_handler (lv_event_t *e) { lv_event_code_t code = lv_event_get_code(e); switch (code) { case LV_EVENT_PRESSED: { break; } default: break; } } static void screen_pull_down_btn_blu_event_handler (lv_event_t *e) { lv_event_code_t code = lv_event_get_code(e); switch (code) { case LV_EVENT_PRESSED: { break; } default: break; } } static void screen_pull_down_btn_set_event_handler (lv_event_t *e) { lv_event_code_t code = lv_event_get_code(e); switch (code) { case LV_EVENT_PRESSED: { ui_nav_load_scr_animation(&guider_ui, &guider_ui.screen_set, guider_ui.screen_set_del, &guider_ui.screen_pull_down_del, setup_scr_screen_set, LV_SCR_LOAD_ANIM_FADE_ON, 150, 0, false, true, UI_NAV_PAGE_IDX_KEEP); break; } default: break; } } static void screen_pull_down_btn_sleep_event_handler (lv_event_t *e) { lv_event_code_t code = lv_event_get_code(e); switch (code) { case LV_EVENT_PRESSED: { ui_nav_load_scr_animation(&guider_ui, &guider_ui.screen_sleep, guider_ui.screen_sleep_del, &guider_ui.screen_pull_down_del, setup_scr_screen_sleep, LV_SCR_LOAD_ANIM_FADE_ON, 150, 0, false, true, UI_NAV_PAGE_IDX_KEEP); break; } default: break; } } static void screen_pull_down_btn_restart_event_handler (lv_event_t *e) { lv_event_code_t code = lv_event_get_code(e); switch (code) { case LV_EVENT_PRESSED: { if (panel_handle) { panel_qspi_amoled_set_brightness(panel_handle, 0); vTaskDelay(pdMS_TO_TICKS(50)); esp_lcd_panel_disp_on_off(panel_handle, false); vTaskDelay(pdMS_TO_TICKS(50)); } esp_restart(); break; } default: break; } } #include "esp_lcd_panel_ops.h" // 必须包含这个头文件 #include "driver/rtc_io.h" static void screen_pull_down_btn_turnoff_event_handler (lv_event_t *e) { lv_event_code_t code = lv_event_get_code(e); switch (code) { case LV_EVENT_CLICKED: { ESP_LOGI("SYSTEM", "正在执行深度关屏程序..."); // 1. 等待手指离开(防止秒醒,这是你之前的改动) while (gpio_get_level(EXAMPLE_PIN_NUM_TOUCH_INT) == 0) { vTaskDelay(pdMS_TO_TICKS(10)); } vTaskDelay(pdMS_TO_TICKS(100)); // 2. 【核心】给屏幕发送关闭显示指令 // 这会让 AMOLED 像素点停止发光,进入低功耗睡眠模式 if (panel_handle != NULL) { esp_lcd_panel_disp_on_off(panel_handle, false); // 关闭显示 } // 3. 【核心】拉低复位引脚并锁定 // IO17 是你的 LCD_RST。在睡觉前把它拉低,强制屏幕处于复位状态 rtc_gpio_init(EXAMPLE_PIN_NUM_LCD_RST); rtc_gpio_set_direction(EXAMPLE_PIN_NUM_LCD_RST, RTC_GPIO_MODE_OUTPUT_ONLY); rtc_gpio_set_level(EXAMPLE_PIN_NUM_LCD_RST, 0); rtc_gpio_hold_en(EXAMPLE_PIN_NUM_LCD_RST); // 即使 CPU 睡着了,IO17 也会一直保持低电平 ESP_LOGI("SYSTEM", "Good night!"); vTaskDelay(pdMS_TO_TICKS(100)); // 5. 正式关机 esp_deep_sleep_start(); break; } default: break; } } void events_init_screen_pull_down (lv_ui *ui) { lv_obj_add_event_cb(ui->screen_pull_down, ui_event_gesture_cb, LV_EVENT_ALL, ui); // lv_obj_add_event_cb(ui->screen_pull_down_btn_Wifi, screen_pull_down_btn_Wifi_event_handler, LV_EVENT_ALL, ui); // lv_obj_add_event_cb(ui->screen_pull_down_btn_blu, screen_pull_down_btn_blu_event_handler, LV_EVENT_ALL, ui); lv_obj_add_event_cb(ui->screen_pull_down_btn_set, screen_pull_down_btn_set_event_handler, LV_EVENT_ALL, ui); lv_obj_add_event_cb(ui->screen_pull_down_btn_sleep, screen_pull_down_btn_sleep_event_handler, LV_EVENT_ALL, ui); lv_obj_add_event_cb(ui->screen_pull_down_btn_restart, screen_pull_down_btn_restart_event_handler, LV_EVENT_ALL, ui); lv_obj_add_event_cb(ui->screen_pull_down_btn_turnoff, screen_pull_down_btn_turnoff_event_handler, LV_EVENT_ALL, ui); } #include "esp_wifi.h" #include "nimble/nimble_port.h" #include "nimble/nimble_port_freertos.h" #include "host/ble_hs.h" #include "services/gap/ble_svc_gap.h" volatile bool is_system_sleeping = false; extern esp_lcd_panel_handle_t panel_handle; // 引用屏幕句柄 extern void ble_app_advertise(void); // 1. 声明外部函数 extern void set_auto_sleep_timeout(uint32_t ms); static void preload_black_frame_before_wakeup(void) { if (panel_handle == NULL) { return; } /* 先把面板 GRAM 清成黑色,避免开屏瞬间显示上一次残留的 sleep 界面 */ static lv_color_t black_rows[EXAMPLE_LCD_H_RES * 8]; for (int y = 0; y < EXAMPLE_LCD_V_RES; y += 8) { int row_count = EXAMPLE_LCD_V_RES - y; if (row_count > 8) { row_count = 8; } esp_lcd_panel_draw_bitmap(panel_handle, 0, y, EXAMPLE_LCD_H_RES, y + row_count, black_rows); } } // 2. 定义时间常量 (毫秒) #define TIMEOUT_30S 30000 #define TIMEOUT_5MIN (5 * 60 * 1000) #define TIMEOUT_10MIN (10 * 60 * 1000) #define TIMEOUT_NEVER 0 // 这会让选中的按钮保持 "CHECKED" 状态(通常变色),其他的变回普通状态 static void update_sleep_btn_ui(lv_obj_t *active_btn, lv_ui *ui) { // 清除所有按钮的选中状态 lv_obj_remove_state(ui->screen_sleep_btn_30s, LV_STATE_CHECKED); lv_obj_remove_state(ui->screen_sleep_btn_5min, LV_STATE_CHECKED); lv_obj_remove_state(ui->screen_sleep_btn_10min, LV_STATE_CHECKED); lv_obj_remove_state(ui->screen_sleep_btn_never, LV_STATE_CHECKED); // 给当前点击的按钮加上选中状态 if (active_btn) { lv_obj_add_state(active_btn, LV_STATE_CHECKED); } } // 进入/退出休眠的控制函数 void toggle_sleep_mode(bool enter_sleep) { if (enter_sleep) { if (is_system_sleeping) return; // 关屏 if (panel_handle) { ESP_LOGI("SLEEP", "System Sleeping..."); // 尝试调用设置亮度函数,不同驱动可能名字不同,通常是这个 panel_qspi_amoled_set_brightness(panel_handle, 0); vTaskDelay(pdMS_TO_TICKS(50)); // 给屏幕一点时间反应 // 2. 发送关闭显示指令 esp_lcd_panel_disp_on_off(panel_handle, false); } // 关 WiFi esp_wifi_stop(); // 如果已连接,断开连接 //ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM); // 停止广播 ble_gap_adv_stop(); is_system_sleeping = true; } else { if (!is_system_sleeping) return; /* 开屏前先立即回到默认页,避免保留在 sleep screen 导致后续手势失效 */ ui_nav_apply_wake_to_lottie_home(); if (guider_ui.screen_pull_down_del || guider_ui.screen_pull_down == NULL) { guider_ui.screen_pull_down = NULL; guider_ui.screen_pull_down_del = true; setup_scr_screen_pull_down(&guider_ui); } preload_black_frame_before_wakeup(); // 开屏 if (panel_handle) { ESP_LOGI("SLEEP", "System Waking up..."); esp_lcd_panel_disp_on_off(panel_handle, true); vTaskDelay(pdMS_TO_TICKS(50)); panel_qspi_amoled_set_brightness(panel_handle, 255); } // 开 WiFi esp_wifi_start(); esp_wifi_connect(); ble_app_advertise(); is_system_sleeping = false; } } static void screen_sleep_btn_10min_event_handler (lv_event_t *e) { lv_event_code_t code = lv_event_get_code(e); lv_ui *ui = (lv_ui *)lv_event_get_user_data(e); switch (code) { case LV_EVENT_CLICKED: case LV_EVENT_PRESSED: { set_auto_sleep_timeout(TIMEOUT_10MIN); commit_auto_sleep_timeout_if_dirty(); update_sleep_btn_ui(ui->screen_sleep_btn_10min, ui); break; } default: break; } } static void screen_sleep_btn_5min_event_handler (lv_event_t *e) { lv_event_code_t code = lv_event_get_code(e); lv_ui *ui = (lv_ui *)lv_event_get_user_data(e); switch (code) { case LV_EVENT_CLICKED: case LV_EVENT_PRESSED: { set_auto_sleep_timeout(TIMEOUT_5MIN); commit_auto_sleep_timeout_if_dirty(); update_sleep_btn_ui(ui->screen_sleep_btn_5min, ui); break; } default: break; } } static void screen_sleep_btn_never_event_handler (lv_event_t *e) { lv_event_code_t code = lv_event_get_code(e); lv_ui *ui = (lv_ui *)lv_event_get_user_data(e); switch (code) { case LV_EVENT_CLICKED: case LV_EVENT_PRESSED: { set_auto_sleep_timeout(TIMEOUT_NEVER); commit_auto_sleep_timeout_if_dirty(); update_sleep_btn_ui(ui->screen_sleep_btn_never, ui); break; } default: break; } } static void screen_sleep_btn_30s_event_handler (lv_event_t *e) { lv_event_code_t code = lv_event_get_code(e); lv_ui *ui = (lv_ui *)lv_event_get_user_data(e); // 获取 UI 句柄 switch (code) { case LV_EVENT_CLICKED: // 建议用 CLICKED 代替 PRESSED case LV_EVENT_PRESSED: { set_auto_sleep_timeout(TIMEOUT_30S); commit_auto_sleep_timeout_if_dirty(); update_sleep_btn_ui(ui->screen_sleep_btn_30s, ui); break; } default: break; } } static void screen_sleep_img_sleep_back_event_handler (lv_event_t *e) { lv_event_code_t code = lv_event_get_code(e); switch (code) { case LV_EVENT_PRESSED: { commit_auto_sleep_timeout_if_dirty(); ui_nav_load_scr_animation(&guider_ui, &guider_ui.screen_pull_down, guider_ui.screen_pull_down_del, &guider_ui.screen_sleep_del, setup_scr_screen_pull_down, LV_SCR_LOAD_ANIM_MOVE_RIGHT, 150, 0, false, true, UI_IDX_PULLDOWN); break; } default: break; } } static void screen_sleep_btn_now_event_handler (lv_event_t *e) { lv_event_code_t code = lv_event_get_code(e); switch (code) { case LV_EVENT_PRESSED: { toggle_sleep_mode(true); break; } default: break; } } extern uint32_t get_auto_sleep_timeout(void); void events_init_screen_sleep (lv_ui *ui) { /* Guider 可能只有 img 或只有 btn 作为返回;当前工程 setup 仅创建 screen_sleep_btn_set_back */ lv_obj_t *sleep_back_target = ui->screen_sleep_img_sleep_back ? ui->screen_sleep_img_sleep_back : ui->screen_sleep_btn_set_back; ESP_LOGD(TAG_SLEEP_EVT, "enter"); ESP_LOGD(TAG_SLEEP_EVT, "ptrs: 10min=%p 5min=%p never=%p 30s=%p img_back=%p btn_set_back=%p back_use=%p now=%p", (void *)ui->screen_sleep_btn_10min, (void *)ui->screen_sleep_btn_5min, (void *)ui->screen_sleep_btn_never, (void *)ui->screen_sleep_btn_30s, (void *)ui->screen_sleep_img_sleep_back, (void *)ui->screen_sleep_btn_set_back, (void *)sleep_back_target, (void *)ui->screen_sleep_btn_now); if (!ui->screen_sleep_btn_10min || !ui->screen_sleep_btn_5min || !ui->screen_sleep_btn_never || !ui->screen_sleep_btn_30s || !sleep_back_target || !ui->screen_sleep_btn_now) { ESP_LOGE(TAG_SLEEP_EVT, "NULL widget ptr (check gui_guider.h vs setup_scr_screen_sleep.c)"); return; } ESP_LOGD(TAG_SLEEP_EVT, "add_event_cb 10min"); lv_obj_add_event_cb(ui->screen_sleep_btn_10min, screen_sleep_btn_10min_event_handler, LV_EVENT_ALL, ui); ESP_LOGD(TAG_SLEEP_EVT, "add_event_cb 5min"); lv_obj_add_event_cb(ui->screen_sleep_btn_5min, screen_sleep_btn_5min_event_handler, LV_EVENT_ALL, ui); ESP_LOGD(TAG_SLEEP_EVT, "add_event_cb never"); lv_obj_add_event_cb(ui->screen_sleep_btn_never, screen_sleep_btn_never_event_handler, LV_EVENT_ALL, ui); ESP_LOGD(TAG_SLEEP_EVT, "add_event_cb 30s"); lv_obj_add_event_cb(ui->screen_sleep_btn_30s, screen_sleep_btn_30s_event_handler, LV_EVENT_ALL, ui); ESP_LOGD(TAG_SLEEP_EVT, "add_event_cb sleep_back (img or btn_set_back)"); lv_obj_add_event_cb(sleep_back_target, screen_sleep_img_sleep_back_event_handler, LV_EVENT_ALL, ui); ESP_LOGD(TAG_SLEEP_EVT, "add_event_cb btn_now"); lv_obj_add_event_cb(ui->screen_sleep_btn_now, screen_sleep_btn_now_event_handler, LV_EVENT_ALL, ui); ESP_LOGD(TAG_SLEEP_EVT, "get_auto_sleep_timeout"); uint32_t current_ms = get_auto_sleep_timeout(); ESP_LOGD(TAG_SLEEP_EVT, "current_ms=%" PRIu32, (uint32_t)current_ms); ESP_LOGD(TAG_SLEEP_EVT, "remove_state all checked"); lv_obj_remove_state(ui->screen_sleep_btn_30s, LV_STATE_CHECKED); lv_obj_remove_state(ui->screen_sleep_btn_5min, LV_STATE_CHECKED); lv_obj_remove_state(ui->screen_sleep_btn_10min, LV_STATE_CHECKED); lv_obj_remove_state(ui->screen_sleep_btn_never, LV_STATE_CHECKED); ESP_LOGD(TAG_SLEEP_EVT, "add_state default branch"); if (current_ms == TIMEOUT_30S) { lv_obj_add_state(ui->screen_sleep_btn_30s, LV_STATE_CHECKED); } else if (current_ms == TIMEOUT_5MIN) { lv_obj_add_state(ui->screen_sleep_btn_5min, LV_STATE_CHECKED); } else if (current_ms == TIMEOUT_10MIN) { lv_obj_add_state(ui->screen_sleep_btn_10min, LV_STATE_CHECKED); } else if (current_ms == TIMEOUT_NEVER) { lv_obj_add_state(ui->screen_sleep_btn_never, LV_STATE_CHECKED); } else { lv_obj_add_state(ui->screen_sleep_btn_30s, LV_STATE_CHECKED); } ESP_LOGD(TAG_SLEEP_EVT, "leave ok"); } extern int get_system_brightness_percent(void); // 2. 滑动条事件回调 static void screen_set_slider_brightness_event_handler(lv_event_t *e) { lv_event_code_t code = lv_event_get_code(e); switch (code) { case LV_EVENT_VALUE_CHANGED: // 数值改变时触发 { lv_obj_t * slider = lv_event_get_target(e); // 获取滑块当前值 (0-100) int val = (int)lv_slider_get_value(slider); // 调用设置函数 set_system_brightness(val); break; } case LV_EVENT_RELEASED: ble_request_send_brightness(); break; default: break; } } static void screen_set_btn_set_back_event_handler (lv_event_t *e) { lv_event_code_t code = lv_event_get_code(e); switch (code) { case LV_EVENT_PRESSED: { commit_brightness_if_dirty(); ui_nav_load_scr_animation(&guider_ui, &guider_ui.screen_pull_down, guider_ui.screen_pull_down_del, &guider_ui.screen_set_del, setup_scr_screen_pull_down, LV_SCR_LOAD_ANIM_MOVE_RIGHT, 150, 0, false, true, UI_IDX_PULLDOWN); break; } default: break; } } // ================= 使用指南翻页数据与逻辑 ================= // 1. 将长文本拆分为 4 页 static const char * guide_pages[] = { // 第 1 页:产品安装 "一、产品安装\n" "1、将身体支架使用附赠3M背胶固定牢固安装于车机屏幕后方,确保支架水平稳定、无松动或晃动。\n" "2、使用支架自带的连接线一端接入中控内端子接口,为机器人提供稳定供电与数据通路。\n", "3、将机器人主机底部标准Type-C接口,对准支架前端预留的Type-C公座,垂直、平稳插入直至完全卡合(有清晰“咔嗒”入位感)完成物理安装与通信连接。\n" "4、安装完毕后,机器人即通过支架获取电力并接入车机系统,无需额外配置。", // 第 2 页:开机与关机 "二、开机与关机\n" "1、本产品采用智能随车电源管理设计,无物理开关。\n" " 全程自动运行:\n" " - 当车辆启动并进入ACC或ON档位(仪表盘通电)时,机器人于3秒内完成自检并自动开机,进入待命状态。\n", " - 当车辆熄火断电后,机器人于30秒内自动进入低功耗休眠模式,停止所有功能响应。(哨兵模式除外)。\n" "2、该机制严格同步车辆电源状态,杜绝误操作与冗余功耗,真正实现“即插即用、上车即控、下车即止。”", // 第 3 页:网络连接 "三、网络连接与系统联动\n" "1、机器人与车机完成首次绑定(通过设置-wifi-点击配置)后,将自动识别、认证并连接车机内置的专用Wi-Fi热点,全程无需手动选择网络或输入密码。\n" "2、连接成功后,APP端显示“成功在线”,即可实时启用以下核心能力", // 第 4 页:功能概览 "四、核心功能概览\n" "1、智能车况表情系统:基于真实车况动态生成友好化表情界面——兼顾信息传达效率与视觉亲和力,让车辆状态“一眼可知、一目了然”。\n", "2、主动情绪陪伴:结合时间、天气、驾驶行为与行程特征,提供有温度的交互体验,以恰到好处的关怀缓解驾驶压力,提升出行幸福感。\n" "3、表情包下载和ota需要热点内打开兼容模式。\n", }; #define GUIDE_TOTAL_PAGES 7 // 2. 定义静态变量用于控制状态 static int current_guide_page = 0; // 当前页码 static lv_obj_t * label_guide_content = NULL; // 用于显示内容的标签对象 static lv_obj_t * label_guide_title = NULL; // 用于显示标题的标签对象 (显示页码) static lv_obj_t * btn_guide_prev = NULL; // 上一页按钮 static lv_obj_t * btn_guide_next = NULL; // 下一页按钮 // 辅助函数:更新界面内容和按钮状态 static void update_guide_ui(void) { // 更新文本内容(不再触发重排) if (label_guide_content) { lv_label_set_text(label_guide_content, guide_pages[current_guide_page]); } // 更新标题 (显示页码 1/4) if (label_guide_title) { char title_buf[32]; snprintf(title_buf, sizeof(title_buf), "产品说明 (%d/%d)", current_guide_page + 1, GUIDE_TOTAL_PAGES); lv_label_set_text(label_guide_title, title_buf); } // 按钮状态控制(你原来的逻辑保持不变) if (btn_guide_prev) { if (current_guide_page == 0) { lv_obj_add_state(btn_guide_prev, LV_STATE_DISABLED); // 禁用状态 lv_obj_set_style_bg_color(btn_guide_prev, lv_color_hex(0x555555), 0); // 变灰 } else { lv_obj_remove_state(btn_guide_prev, LV_STATE_DISABLED); lv_obj_set_style_bg_color(btn_guide_prev, lv_color_hex(0x56D695), 0); } } if (btn_guide_next) { if (current_guide_page == GUIDE_TOTAL_PAGES - 1) { lv_obj_add_state(btn_guide_next, LV_STATE_DISABLED); lv_obj_set_style_bg_color(btn_guide_next, lv_color_hex(0x555555), 0); // 变灰 } else { lv_obj_remove_state(btn_guide_next, LV_STATE_DISABLED); lv_obj_set_style_bg_color(btn_guide_next, lv_color_hex(0x56D695), 0); } } } // 按钮回调:上一页 static void guide_prev_event_handler(lv_event_t * e) { if (current_guide_page > 0) { current_guide_page--; update_guide_ui(); } } // 按钮回调:下一页 static void guide_next_event_handler(lv_event_t * e) { if (current_guide_page < GUIDE_TOTAL_PAGES - 1) { current_guide_page++; update_guide_ui(); } } // [辅助函数] 用于关闭消息框 static void msgbox_close_event_handler(lv_event_t * e) { lv_obj_t * mbox = (lv_obj_t *)lv_event_get_user_data(e); if(mbox && lv_obj_is_valid(mbox)) { lv_msgbox_close(mbox); // LVGL v9 关闭消息框的专用函数 } } /** 设置页 msgbox 底部按钮:垂直加高;使用说明三键为 iconfont(见 `GUIDE_FOOTER_ICON_*`) */ #define SCREEN_SET_MSGBOX_BTN_PAD_VER 24 #define SCREEN_SET_MSGBOX_BTN_PAD_HOR 22 /** 使用说明「上一页/下一页/关闭」:与版本信息按钮大小保持一致 */ #define SCREEN_SET_MSGBOX_BTN_PAD_HOR_GUIDE 22 #define SCREEN_SET_MSGBOX_BTN_PAD_VER_GUIDE 24 /** iconfont(`lv_font_iconfont_25.c`):U+E644 、U+E66C 、U+E655  */ #define GUIDE_FOOTER_ICON_PREV "\xEE\x99\x84" #define GUIDE_FOOTER_ICON_NEXT "\xEE\x99\xAC" #define GUIDE_FOOTER_ICON_CLOSE "\xEE\x99\x95" /** 版本信息 / 系统提示等 msgbox:U+E67C (取消)、U+E64B (确定) */ #define MSGBOX_FOOTER_ICON_CANCEL "\xEE\x99\xBC" #define MSGBOX_FOOTER_ICON_OK "\xEE\x99\x8B" /** 版本信息 / 系统提示等 footer 按钮:图标字必须用 lv_font_iconfont_25(与按钮、label 均显式设置,避免继承 MiSans) */ static void screen_set_msgbox_footer_btn_label_iconfont(lv_obj_t *btn) { if (btn == NULL) { return; } const lv_style_selector_t sel_d = LV_PART_MAIN | LV_STATE_DEFAULT; const lv_style_selector_t sel_p = LV_PART_MAIN | LV_STATE_PRESSED; const lv_style_selector_t sel_c = LV_PART_MAIN | LV_STATE_CHECKED; const lv_style_selector_t sel_dis = LV_PART_MAIN | LV_STATE_DISABLED; const lv_style_selector_t sel_f = LV_PART_MAIN | LV_STATE_FOCUSED; lv_obj_set_style_text_font(btn, &lv_font_iconfont_25, sel_d); lv_obj_set_style_text_font(btn, &lv_font_iconfont_25, sel_p); lv_obj_set_style_text_font(btn, &lv_font_iconfont_25, sel_c); lv_obj_set_style_text_font(btn, &lv_font_iconfont_25, sel_dis); lv_obj_set_style_text_font(btn, &lv_font_iconfont_25, sel_f); lv_obj_t *lbl = lv_obj_get_child(btn, 0); if (lbl != NULL && lv_obj_is_valid(lbl)) { lv_obj_set_style_text_font(lbl, &lv_font_iconfont_25, sel_d); lv_obj_set_style_text_font(lbl, &lv_font_iconfont_25, sel_dis); lv_obj_set_style_text_font(lbl, &lv_font_iconfont_25, sel_p); lv_obj_set_style_text_font(lbl, &lv_font_iconfont_25, sel_c); lv_obj_set_style_text_font(lbl, &lv_font_iconfont_25, sel_f); } } static void screen_set_apply_msgbox_footer_btn_size(lv_obj_t *btn) { if (btn == NULL) { return; } lv_obj_set_style_pad_hor(btn, SCREEN_SET_MSGBOX_BTN_PAD_HOR, 0); lv_obj_set_style_pad_ver(btn, SCREEN_SET_MSGBOX_BTN_PAD_VER, 0); } static void screen_set_apply_msgbox_footer_btn_size_guide(lv_obj_t *btn) { if (btn == NULL) { return; } lv_obj_set_style_pad_hor(btn, SCREEN_SET_MSGBOX_BTN_PAD_HOR_GUIDE, 0); lv_obj_set_style_pad_ver(btn, SCREEN_SET_MSGBOX_BTN_PAD_VER_GUIDE, 0); } static void screen_set_btn_guide_event_handler (lv_event_t *e) { lv_event_code_t code = lv_event_get_code(e); switch (code) { case LV_EVENT_CLICKED: { // 1. 重置页码 current_guide_page = 0; // 2. 创建消息框 lv_obj_t * mbox = lv_msgbox_create(NULL); // 3. 初始化标题和内容 // 捕获对象指针以便后续更新 label_guide_title = lv_msgbox_add_title(mbox, "产品说明 (1/4)"); label_guide_content = lv_msgbox_add_text(mbox, guide_pages[0]); lv_obj_set_size(label_guide_content, 390, 285); lv_label_set_long_mode(label_guide_content, LV_LABEL_LONG_WRAP); // ================= 按钮区域 ================= btn_guide_prev = lv_msgbox_add_footer_button(mbox, GUIDE_FOOTER_ICON_PREV); lv_obj_add_event_cb(btn_guide_prev, guide_prev_event_handler, LV_EVENT_CLICKED, NULL); screen_set_msgbox_footer_btn_label_iconfont(btn_guide_prev); btn_guide_next = lv_msgbox_add_footer_button(mbox, GUIDE_FOOTER_ICON_NEXT); lv_obj_add_event_cb(btn_guide_next, guide_next_event_handler, LV_EVENT_CLICKED, NULL); screen_set_msgbox_footer_btn_label_iconfont(btn_guide_next); lv_obj_t * btn_close = lv_msgbox_add_footer_button(mbox, GUIDE_FOOTER_ICON_CLOSE); lv_obj_add_event_cb(btn_close, msgbox_close_event_handler, LV_EVENT_CLICKED, mbox); screen_set_msgbox_footer_btn_label_iconfont(btn_close); // ================= 样式美化 ================= // --- A. mbox 整体设置 --- lv_obj_set_width(mbox, 502); lv_obj_set_height(mbox, 410); lv_obj_center(mbox); // 背景全黑 lv_obj_set_style_bg_color(mbox, lv_color_hex(0x000000), 0); lv_obj_set_style_bg_opa(mbox, LV_OPA_COVER, 0); lv_obj_set_style_border_width(mbox, 0, 0); /* 勿对 mbox 设置 text_font,避免继承覆盖 footer 的 iconfont(否则 PUA 显示为方框) */ lv_obj_set_style_text_line_space(mbox, 10, 0); // 增加行间距,阅读更舒适 lv_obj_set_style_text_color(mbox, lv_color_hex(0xFFFFFF), 0); lv_obj_set_style_text_font(label_guide_title, &lv_font_montserrat_14, 0); lv_obj_set_style_text_font(label_guide_content, &lv_font_montserrat_14, 0); // --- B. 标题区域设置 (Header) --- // 解决标题背景白色的问题 lv_obj_t * header = lv_msgbox_get_header(mbox); if (header) { lv_obj_set_style_bg_color(header, lv_color_hex(0x000000), 0); // 强制黑底 lv_obj_set_style_bg_opa(header, LV_OPA_COVER, 0); lv_obj_set_style_pad_left(header, 30, 0); // 标题左移一点 } // 单独设置标题文字颜色 lv_obj_set_style_text_color(label_guide_title, lv_color_hex(0x000000), 0); // --- C. 内容区域设置 (Content) --- lv_obj_t * content = lv_msgbox_get_content(mbox); if (content) { lv_obj_set_style_bg_color(content, lv_color_hex(0x000000), 0); lv_obj_set_style_pad_left(content, 50, 0); lv_obj_set_style_pad_bottom(content, 6, 0); } // --- D. 按钮区域设置 (Footer) --- lv_obj_t * footer = lv_msgbox_get_footer(mbox); if (footer) { lv_obj_set_style_bg_color(footer, lv_color_hex(0x000000), 0); lv_obj_set_flex_flow(footer, LV_FLEX_FLOW_ROW); /* 三键横向均匀分布(首尾留白与键间距一致) */ lv_obj_set_flex_align(footer, LV_FLEX_ALIGN_SPACE_EVENLY, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); lv_obj_set_style_pad_column(footer, 0, 0); lv_obj_set_style_pad_left(footer, 6, 0); lv_obj_set_style_pad_right(footer, 6, 0); lv_obj_set_style_pad_top(footer, 4, 0); } // --- E. 按钮本身样式 --- // 上一页按钮 (淡绿色);按下/选中时与常态完全一致(不显式变化) lv_obj_set_size(btn_guide_prev, 100, 30); lv_obj_set_style_bg_color(btn_guide_prev, lv_color_hex(0x56D695), 0); lv_obj_set_style_text_color(btn_guide_prev, lv_color_hex(0xFFFFFF), 0); lv_obj_set_style_radius(btn_guide_prev, 20, 0); lv_obj_set_style_translate_x(btn_guide_prev, 20, 0); // 向右偏移20像素 screen_set_apply_msgbox_footer_btn_size_guide(btn_guide_prev); { const lv_style_selector_t sel_p = LV_PART_MAIN | LV_STATE_PRESSED; const lv_style_selector_t sel_c = LV_PART_MAIN | LV_STATE_CHECKED; lv_obj_set_style_bg_color(btn_guide_prev, lv_color_hex(0x56D695), sel_p); lv_obj_set_style_bg_color(btn_guide_prev, lv_color_hex(0x56D695), sel_c); lv_obj_set_style_bg_opa(btn_guide_prev, LV_OPA_COVER, sel_p); lv_obj_set_style_bg_opa(btn_guide_prev, LV_OPA_COVER, sel_c); lv_obj_set_style_text_color(btn_guide_prev, lv_color_hex(0xFFFFFF), sel_p); lv_obj_set_style_text_color(btn_guide_prev, lv_color_hex(0xFFFFFF), sel_c); lv_obj_set_style_radius(btn_guide_prev, 20, sel_p); lv_obj_set_style_radius(btn_guide_prev, 20, sel_c); lv_obj_set_style_pad_hor(btn_guide_prev, SCREEN_SET_MSGBOX_BTN_PAD_HOR_GUIDE, sel_p); lv_obj_set_style_pad_hor(btn_guide_prev, SCREEN_SET_MSGBOX_BTN_PAD_HOR_GUIDE, sel_c); lv_obj_set_style_pad_ver(btn_guide_prev, SCREEN_SET_MSGBOX_BTN_PAD_VER_GUIDE, sel_p); lv_obj_set_style_pad_ver(btn_guide_prev, SCREEN_SET_MSGBOX_BTN_PAD_VER_GUIDE, sel_c); lv_obj_set_style_border_width(btn_guide_prev, 0, sel_p); lv_obj_set_style_border_width(btn_guide_prev, 0, sel_c); lv_obj_set_style_shadow_width(btn_guide_prev, 0, sel_p); lv_obj_set_style_shadow_width(btn_guide_prev, 0, sel_c); lv_obj_set_style_translate_x(btn_guide_prev, 20, sel_p); lv_obj_set_style_translate_y(btn_guide_prev, 0, sel_p); lv_obj_set_style_translate_x(btn_guide_prev, 20, sel_c); lv_obj_set_style_translate_y(btn_guide_prev, 0, sel_c); } // 下一页按钮 (淡绿色);按下/选中时与常态完全一致(不显式变化) lv_obj_set_size(btn_guide_next, 100, 30); lv_obj_set_style_bg_color(btn_guide_next, lv_color_hex(0x56D695), 0); lv_obj_set_style_text_color(btn_guide_next, lv_color_hex(0xFFFFFF), 0); lv_obj_set_style_radius(btn_guide_next, 20, 0); screen_set_apply_msgbox_footer_btn_size_guide(btn_guide_next); { const lv_style_selector_t sel_p = LV_PART_MAIN | LV_STATE_PRESSED; const lv_style_selector_t sel_c = LV_PART_MAIN | LV_STATE_CHECKED; lv_obj_set_style_bg_color(btn_guide_next, lv_color_hex(0x56D695), sel_p); lv_obj_set_style_bg_color(btn_guide_next, lv_color_hex(0x56D695), sel_c); lv_obj_set_style_bg_opa(btn_guide_next, LV_OPA_COVER, sel_p); lv_obj_set_style_bg_opa(btn_guide_next, LV_OPA_COVER, sel_c); lv_obj_set_style_text_color(btn_guide_next, lv_color_hex(0xFFFFFF), sel_p); lv_obj_set_style_text_color(btn_guide_next, lv_color_hex(0xFFFFFF), sel_c); lv_obj_set_style_radius(btn_guide_next, 20, sel_p); lv_obj_set_style_radius(btn_guide_next, 20, sel_c); lv_obj_set_style_pad_hor(btn_guide_next, SCREEN_SET_MSGBOX_BTN_PAD_HOR_GUIDE, sel_p); lv_obj_set_style_pad_hor(btn_guide_next, SCREEN_SET_MSGBOX_BTN_PAD_HOR_GUIDE, sel_c); lv_obj_set_style_pad_ver(btn_guide_next, SCREEN_SET_MSGBOX_BTN_PAD_VER_GUIDE, sel_p); lv_obj_set_style_pad_ver(btn_guide_next, SCREEN_SET_MSGBOX_BTN_PAD_VER_GUIDE, sel_c); lv_obj_set_style_border_width(btn_guide_next, 0, sel_p); lv_obj_set_style_border_width(btn_guide_next, 0, sel_c); lv_obj_set_style_shadow_width(btn_guide_next, 0, sel_p); lv_obj_set_style_shadow_width(btn_guide_next, 0, sel_c); lv_obj_set_style_translate_x(btn_guide_next, 0, sel_p); lv_obj_set_style_translate_y(btn_guide_next, 0, sel_p); lv_obj_set_style_translate_x(btn_guide_next, 0, sel_c); lv_obj_set_style_translate_y(btn_guide_next, 0, sel_c); } // 关闭按钮 (灰色);按下/选中时与常态完全一致(不显式变化) lv_obj_set_size(btn_close, 100, 30); lv_obj_set_style_bg_color(btn_close, lv_color_hex(0x808080), 0); lv_obj_set_style_text_color(btn_close, lv_color_hex(0xFFFFFF), 0); lv_obj_set_style_radius(btn_close, 20, 0); lv_obj_set_style_translate_x(btn_close, -20, 0); // 向左偏移20像素 screen_set_apply_msgbox_footer_btn_size_guide(btn_close); { const lv_style_selector_t sel_p = LV_PART_MAIN | LV_STATE_PRESSED; const lv_style_selector_t sel_c = LV_PART_MAIN | LV_STATE_CHECKED; lv_obj_set_style_bg_color(btn_close, lv_color_hex(0x808080), sel_p); lv_obj_set_style_bg_color(btn_close, lv_color_hex(0x808080), sel_c); lv_obj_set_style_bg_opa(btn_close, LV_OPA_COVER, sel_p); lv_obj_set_style_bg_opa(btn_close, LV_OPA_COVER, sel_c); lv_obj_set_style_text_color(btn_close, lv_color_hex(0xFFFFFF), sel_p); lv_obj_set_style_text_color(btn_close, lv_color_hex(0xFFFFFF), sel_c); lv_obj_set_style_radius(btn_close, 20, sel_p); lv_obj_set_style_radius(btn_close, 20, sel_c); lv_obj_set_style_pad_hor(btn_close, SCREEN_SET_MSGBOX_BTN_PAD_HOR_GUIDE, sel_p); lv_obj_set_style_pad_hor(btn_close, SCREEN_SET_MSGBOX_BTN_PAD_HOR_GUIDE, sel_c); lv_obj_set_style_pad_ver(btn_close, SCREEN_SET_MSGBOX_BTN_PAD_VER_GUIDE, sel_p); lv_obj_set_style_pad_ver(btn_close, SCREEN_SET_MSGBOX_BTN_PAD_VER_GUIDE, sel_c); lv_obj_set_style_border_width(btn_close, 0, sel_p); lv_obj_set_style_border_width(btn_close, 0, sel_c); lv_obj_set_style_shadow_width(btn_close, 0, sel_p); lv_obj_set_style_shadow_width(btn_close, 0, sel_c); lv_obj_set_style_translate_x(btn_close, -20, sel_p); lv_obj_set_style_translate_y(btn_close, 0, sel_p); lv_obj_set_style_translate_x(btn_close, -20, sel_c); lv_obj_set_style_translate_y(btn_close, 0, sel_c); } screen_set_msgbox_footer_btn_label_iconfont(btn_guide_prev); screen_set_msgbox_footer_btn_label_iconfont(btn_guide_next); screen_set_msgbox_footer_btn_label_iconfont(btn_close); // 4. 初始化 UI 状态 (应用第一页的禁用逻辑) update_guide_ui(); break; } default: break; } } static void screen_set_btn_information_event_handler (lv_event_t *e) { lv_event_code_t code = lv_event_get_code(e); switch (code) { case LV_EVENT_CLICKED: { const char * version_body = "软件版本号:v1.0.3\n" "版本类型:首发正式版\n" "发布内容:\n" "(1)实现车载机器人基础功能的完整运行与控制\n" "(2)完成与车机端的通信连接与指令交互\n" "(3)支持基础状态显示与功能响应\n" "(4)系统运行稳定,满足当前阶段的功能与性能要求"; // 1. 创建消息框 lv_obj_t * mbox = lv_msgbox_create(NULL); // 2. 正文:首行「情绪机器人」单独居中,其余左对齐 lv_obj_t * content = lv_msgbox_get_content(mbox); lv_obj_t * title_label = lv_label_create(content); lv_label_set_text(title_label, "情绪机器人"); lv_obj_set_width(title_label, lv_pct(100)); lv_obj_set_style_text_align(title_label, LV_TEXT_ALIGN_CENTER, 0); lv_obj_set_style_text_color(title_label, lv_color_hex(0xFFFFFF), 0); lv_obj_t * content_label = lv_label_create(content); lv_label_set_text(content_label, version_body); lv_obj_set_width(content_label, lv_pct(100)); lv_obj_set_style_text_align(content_label, LV_TEXT_ALIGN_LEFT, 0); lv_obj_set_style_text_color(content_label, lv_color_hex(0xFFFFFF), 0); // 3. 添加按钮(取消 → iconfont ) lv_obj_t * btn = lv_msgbox_add_footer_button(mbox, "取消"); lv_obj_add_event_cb(btn, msgbox_close_event_handler, LV_EVENT_CLICKED, mbox); lv_obj_set_style_text_font(btn, &lv_font_montserrat_14, 0); // ================= 样式设置 ================= // --- A. mbox 整体设置 --- lv_obj_set_width(mbox, 502); lv_obj_set_height(mbox, 410); lv_obj_center(mbox); // 背景全黑 lv_obj_set_style_bg_color(mbox, lv_color_hex(0x000000), 0); lv_obj_set_style_bg_opa(mbox, LV_OPA_COVER, 0); lv_obj_set_style_border_width(mbox, 0, 0); // 去掉边框 /* 勿对 mbox 设置 text_font:可继承到 footer 按钮,MiSans 无 PUA 字形会显示为方框;正文仅用 MiSans */ lv_obj_set_style_text_line_space(mbox, 8, 0); lv_obj_set_style_text_color(mbox, lv_color_hex(0xFFFFFF), 0); if (title_label) { lv_obj_set_style_text_font(title_label, &lv_font_montserrat_14, 0); } if (content_label) { lv_obj_set_style_text_font(content_label, &lv_font_montserrat_14, 0); lv_obj_set_style_text_line_space(content_label, 8, 0); } // --- B. 内容区域 (Content):与左、上边缘约 20px if (content) { lv_obj_set_style_bg_color(content, lv_color_hex(0x000000), 0); lv_obj_set_style_pad_left(content, 20, 0); lv_obj_set_style_pad_top(content, 20, 0); /* 版本信息正文区域高度减少 20,footer 会随布局整体上移 */ lv_obj_update_layout(mbox); int32_t content_h = lv_obj_get_height(content); if (content_h > 20) { lv_obj_set_height(content, content_h - 20); } } lv_obj_remove_flag(mbox, LV_OBJ_FLAG_SCROLLABLE); { lv_obj_t * mbox_parent = lv_obj_get_parent(mbox); if (mbox_parent) { lv_obj_remove_flag(mbox_parent, LV_OBJ_FLAG_SCROLLABLE); } } if (content) { lv_obj_remove_flag(content, LV_OBJ_FLAG_SCROLLABLE); } if (title_label) { lv_obj_remove_flag(title_label, LV_OBJ_FLAG_SCROLLABLE); } if (content_label) { lv_obj_remove_flag(content_label, LV_OBJ_FLAG_SCROLLABLE); } // --- C. 按钮区域设置 (Footer) --- lv_obj_set_style_bg_color(btn, lv_color_hex(0x808080), 0); lv_obj_set_style_text_color(btn, lv_color_hex(0xFFFFFF), 0); lv_obj_set_style_radius(btn, 20, 0); screen_set_apply_msgbox_footer_btn_size(btn); break; } default: break; } } // 确保声明了外部函数 (如果文件顶部没写) extern void set_auto_sleep_timeout(uint32_t ms); extern void set_system_brightness(int percent); // 恢复出厂设置的确认回调函数 static void factory_reset_confirm_cb(lv_event_t *e) { lv_obj_t *mbox = (lv_obj_t *)lv_event_get_user_data(e); // 关闭消息框 if(mbox && lv_obj_is_valid(mbox)) { lv_msgbox_close(mbox); } // ================= 执行恢复出厂设置逻辑 ================= /* 恢复休眠为 0 分钟并立即写入 NVS。仅用 set_auto_sleep_timeout 会在 50ms 定时器里写盘, * esp_restart 太快时回调来不及执行,且若当前已是 300000 会直接 return 导致永不落盘。 */ persist_auto_sleep_timeout_ms_now(0); // 2. 恢复亮度为 80% set_system_brightness(80); // 3. 刷新亮度滑块 UI if (guider_ui.screen_set_slider_light != NULL && lv_obj_is_valid(guider_ui.screen_set_slider_light)) { lv_slider_set_value(guider_ui.screen_set_slider_light, 75, LV_ANIM_ON); } // 4. 重启设备以完成恢复 if (panel_handle) { panel_qspi_amoled_set_brightness(panel_handle, 0); vTaskDelay(pdMS_TO_TICKS(50)); esp_lcd_panel_disp_on_off(panel_handle, false); vTaskDelay(pdMS_TO_TICKS(50)); } esp_restart(); } static void screen_set_btn_set_recover_event_handler (lv_event_t *e) { lv_event_code_t code = lv_event_get_code(e); switch (code) { case LV_EVENT_CLICKED: // 建议用 CLICKED { // ================= 弹出确认对话框 ================= lv_obj_t * mbox = lv_msgbox_create(NULL); lv_obj_t * title = lv_msgbox_add_title(mbox, "系统提示"); lv_obj_t * content = lv_msgbox_add_text(mbox, "是否恢复出厂设置?\n\n恢复后将重置所有设置并重启设备"); //内容居中 lv_obj_set_style_text_align(content, LV_TEXT_ALIGN_CENTER, 0); // 按钮(确定 → ,取消 → ) lv_obj_t * btn1 = lv_msgbox_add_footer_button(mbox, "确认"); lv_obj_t * btn2 = lv_msgbox_add_footer_button(mbox, "取消"); lv_obj_add_event_cb(btn1, factory_reset_confirm_cb, LV_EVENT_CLICKED, mbox); lv_obj_add_event_cb(btn2, msgbox_close_event_handler, LV_EVENT_CLICKED, mbox); lv_obj_set_style_text_font(btn1, &lv_font_montserrat_14, 0); lv_obj_set_style_text_font(btn2, &lv_font_montserrat_14, 0); // --- 样式美化 (保持全黑风格) --- lv_obj_set_width(mbox, 400); lv_obj_center(mbox); // 弹窗外部遮罩改为纯黑 lv_obj_t * backdrop = lv_obj_get_parent(mbox); if(backdrop) { lv_obj_set_style_bg_color(backdrop, lv_color_hex(0x000000), 0); lv_obj_set_style_bg_opa(backdrop, LV_OPA_COVER, 0); lv_obj_set_style_border_width(backdrop, 0, 0); } // 弹窗本体黑底无边框 lv_obj_set_style_bg_color(mbox, lv_color_hex(0x000000), 0); lv_obj_set_style_bg_opa(mbox, LV_OPA_COVER, 0); lv_obj_set_style_border_width(mbox, 0, 0); lv_obj_set_style_border_color(mbox, lv_color_hex(0xFFFFFF), 0); lv_obj_set_style_border_opa(mbox, LV_OPA_COVER, 0); lv_obj_set_style_border_side(mbox, LV_BORDER_SIDE_FULL, 0); // 字体与颜色(勿对 mbox 设 text_font,避免 footer 图标被 MiSans 继承成方框) lv_obj_set_style_text_color(mbox, lv_color_hex(0xFFFFFF), 0); // 全局白字 lv_obj_set_style_text_font(title, &lv_font_montserrat_14, 0); lv_obj_set_style_text_font(content, &lv_font_montserrat_14, 0); // 单独处理标题颜色 lv_obj_set_style_text_color(title, lv_color_hex(0x00E0FF), 0); // 标题蓝字 // 处理标题背景 lv_obj_t * header = lv_msgbox_get_header(mbox); if(header) { lv_obj_set_style_bg_color(header, lv_color_hex(0x000000), 0); lv_obj_set_style_bg_opa(header, LV_OPA_COVER, 0); } // 处理底部按钮栏背景 lv_obj_t * footer = lv_msgbox_get_footer(mbox); if(footer) { lv_obj_set_style_bg_color(footer, lv_color_hex(0x000000), 0); // 按钮居中 lv_obj_set_flex_align(footer, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); lv_obj_set_style_pad_column(footer, 30, 0); } lv_obj_set_style_bg_color(btn1, lv_color_hex(0x56D695), 0); lv_obj_set_style_text_color(btn1, lv_color_hex(0xFFFFFF), 0); lv_obj_set_style_radius(btn1, 20, 0); screen_set_apply_msgbox_footer_btn_size_guide(btn1); lv_obj_set_style_bg_color(btn2, lv_color_hex(0x808080), 0); lv_obj_set_style_text_color(btn2, lv_color_hex(0xFFFFFF), 0); lv_obj_set_style_radius(btn2, 20, 0); screen_set_apply_msgbox_footer_btn_size_guide(btn2); break; } default: break; } } void events_init_screen_set (lv_ui *ui) { lv_obj_add_event_cb(ui->screen_set_btn_guide, screen_set_btn_guide_event_handler, LV_EVENT_ALL, ui); lv_obj_add_event_cb(ui->screen_set_btn_information, screen_set_btn_information_event_handler, LV_EVENT_ALL, ui); lv_obj_add_event_cb(ui->screen_set_btn_set_recover, screen_set_btn_set_recover_event_handler, LV_EVENT_ALL, ui); lv_obj_add_event_cb(ui->screen_set_btn_set_back, screen_set_btn_set_back_event_handler, LV_EVENT_ALL, ui); if (ui->screen_set_slider_light != NULL) { lv_obj_add_event_cb(ui->screen_set_slider_light, screen_set_slider_brightness_event_handler, LV_EVENT_ALL, ui); int current_percent = get_system_brightness_percent(); lv_slider_set_value(ui->screen_set_slider_light, current_percent, LV_ANIM_OFF); } } static void screen_component_event_handler (lv_event_t *e) { lv_event_code_t code = lv_event_get_code(e); switch (code) { case LV_EVENT_GESTURE: { if (!is_gesture_allowed()) return; // 防抖检查 lv_dir_t dir = lv_indev_get_gesture_dir(lv_indev_active()); switch(dir) { case LV_DIR_LEFT: { lv_indev_wait_release(lv_indev_active()); ui_nav_load_scr_animation(&guider_ui, &guider_ui.screen_home_weather, guider_ui.screen_home_weather_del, &guider_ui.screen_component_del, setup_scr_screen_home_weather, LV_SCR_LOAD_ANIM_MOVE_LEFT, 150, 0, false, true, 1); break; } case LV_DIR_RIGHT: { lv_indev_wait_release(lv_indev_active()); ui_nav_load_scr_animation(&guider_ui, &guider_ui.screen_time, guider_ui.screen_time_del, &guider_ui.screen_component_del, setup_scr_screen_time, LV_SCR_LOAD_ANIM_MOVE_RIGHT, 150, 0, false, true, 2); break; } case LV_DIR_BOTTOM: { lv_indev_wait_release(lv_indev_active()); ui_nav_load_scr_animation(&guider_ui, &guider_ui.screen_pull_down, guider_ui.screen_pull_down_del, &guider_ui.screen_component_del, setup_scr_screen_pull_down, LV_SCR_LOAD_ANIM_OVER_BOTTOM, 150, 0, false, true, UI_IDX_PULLDOWN); break; } default: break; } break; } default: break; } } static void screen_component_btn_chouqian_event_handler (lv_event_t *e) { lv_event_code_t code = lv_event_get_code(e); switch (code) { case LV_EVENT_PRESSED: { ESP_LOGD("按钮","每日抽签"); ui_nav_go_by_index(0, LV_SCR_LOAD_ANIM_FADE_ON); ui_nav_request_emoji_event(emoji_event_bless); // 抽签结果的 UI 展示改到 lottie 播完后的回调里统一处理 //ui_nav_load_scr_animation(&guider_ui, &guider_ui.screen_sortition, guider_ui.screen_sortition_del, &guider_ui.screen_component_del, setup_scr_screen_sortition, LV_SCR_LOAD_ANIM_FADE_ON, 200, 100, false, true, UI_NAV_PAGE_IDX_KEEP); break; } default: break; } } static void screen_component_btn_xingzuo_event_handler (lv_event_t *e) { lv_event_code_t code = lv_event_get_code(e); switch (code) { case LV_EVENT_PRESSED: { ESP_LOGD("按钮","星座"); ui_nav_go_by_index(0, LV_SCR_LOAD_ANIM_FADE_ON); ui_nav_request_emoji_event(emoji_event_horoscope); break; } default: break; } } static void screen_component_btn_zhengnian_event_handler (lv_event_t *e) { lv_event_code_t code = lv_event_get_code(e); switch (code) { case LV_EVENT_PRESSED: { ESP_LOGD("按钮","正念"); // 方案二:使用状态标志控制手势检测 ui_nav_go_by_index(0, LV_SCR_LOAD_ANIM_NONE); // 设置正念活跃标志,关闭手势检测 g_mindfulness_active = true; ESP_LOGI("mindfulness", "start playing, gesture disabled"); // 播放正念表情 ui_nav_request_emoji_event(emoji_event_mindfulness); break; } default: break; } } static void screen_component_btn_calendar_event_handler (lv_event_t *e) { lv_event_code_t code = lv_event_get_code(e); switch (code) { case LV_EVENT_PRESSED: { ESP_LOGD("按钮","万年历"); ui_nav_load_scr_animation(&guider_ui, &guider_ui.screen_Perpetual_Calendar, guider_ui.screen_Perpetual_Calendar_del, &guider_ui.screen_component_del, setup_scr_screen_Perpetual_Calendar, LV_SCR_LOAD_ANIM_FADE_ON, 150, 0, false, true, UI_NAV_PAGE_IDX_KEEP); ui_dispatch_sync_perpetual_calendar_from_time(); break; } default: break; } } void events_init_screen_component (lv_ui *ui) { lv_obj_add_event_cb(ui->screen_component, ui_event_gesture_cb, LV_EVENT_ALL, ui); lv_obj_add_event_cb(ui->screen_component_btn_chouqian, screen_component_btn_chouqian_event_handler, LV_EVENT_ALL, ui); lv_obj_add_event_cb(ui->screen_component_btn_xingzuo, screen_component_btn_xingzuo_event_handler, LV_EVENT_ALL, ui); lv_obj_add_event_cb(ui->screen_component_btn_zhengnian, screen_component_btn_zhengnian_event_handler, LV_EVENT_ALL, ui); lv_obj_add_event_cb(ui->screen_component_btn_calendar, screen_component_btn_calendar_event_handler, LV_EVENT_ALL, ui); } // 定义星座运势库 static const char * const fortunes[] = { "行动力满格出行避冲动,小财进账事事皆顺意", "精神物质双富足,社交沟通顺风顺水无阻碍", "今日运势佳,开车别急躁做事稳一点更顺遂", "遇财务小惊喜,出行路况顺把握当下好机会", "宇宙送好运财运超棒,日常不宜冲动去消费", "沟通运超佳正偏财皆来,出行宜轻装更省心", "耐心处事情绪别起伏,出行避拥堵万事顺意", "今日运势超亮眼,开车稳行平安伴左右", "创意大爆发小金库看涨,展才华收获满惊喜", "财运佳出行一路顺,展自身才华有意外收获", "细节定成败做事宜细致,出行多留意路况稳", "人际超和谐有意外小财,出行宜结伴更舒心", "直觉超敏锐烦恼全消散,出行遇贵人少弯路", "迎远方喜报幸运值拉满,出行宜放松享美好", "稳步前行想做皆有回响,职场遇新机有突破", "脑洞大开灵感不停歇,人际有惊喜出行宜随心", "日常遇小确幸内心超平静,出行宜慢走赏风景", "水逆退散霉运全清零,出行顺利社交节节升", "锦鲤附身好运常相伴,小财入账生活皆顺意", "贵人运爆棚遇事有人帮,开车稳行一路皆坦途", "社交迎新机把握有收获,出行早出发避高峰", "财运稳步涨理财有小成,出行少变道更安全", "心态放平和事事皆顺意,开车不赶路享沿途", "职场遇伯乐才华被看见,出行路况佳无阻碍", "偏财运来袭有意外惊喜,出行宜谨慎保平安", "事事皆顺意烦恼全远离,开车守规则一路畅通", "能量满格做事效率高,小财进账出行皆顺利", "人际遇良缘相处超和谐,出行宜结伴添乐趣", "社交稳步走收获在眼前,出行避弯路更省时", "财运有惊喜消费宜理性,开车保持好心态更顺", "今日福运浓,凡事皆顺意出行平安财运稳", "压力全减轻心想皆事成,开车心放宽路路通", "财源缓缓来工作有进步,出行无波折事事顺", "家庭超和谐社交破瓶颈,出行遇顺境少烦恼", "吉兆自天成霉运全退散,开车多留意皆平安", "大笔小进账诸事皆顺意,出行宜慢行享美好", "正桃花降临治愈渐升华,出行遇美好多欢喜", "心想皆事成付出有收获,出行一路顺无阻碍", "生活无忧愁霉运全扫除,开车心轻松事事顺", "社交攀新高财运跟着涨,出行早规划避拥堵", "桃花朵朵开生活多甜蜜,出行宜舒心享时光", "梦想逐步现好运连连来,出行无难事皆顺意", "努力有回响收获在身边,开车稳行平安相伴", "凡事皆顺意好运常围绕,小财入账生活多美好", "思路开新局做事有方向,出行避高峰更顺畅", "职场遇良机把握能出彩,出行路况佳无波折", "情绪超稳定做事更高效,出行宜随心多顺遂", "好运藏努力付出有回报,开车守交规一路平安", "惊喜常相伴生活多美好,出行遇温暖少烦恼", "福运滚滚来诸事皆顺意,开车享美好一路坦途", }; #define FORTUNE_COUNT (sizeof(fortunes) / sizeof(fortunes[0])) static void update_fortune_text(const char *new_text) { if (guider_ui.screen_fortune_text_label_fotune != NULL && lv_obj_is_valid(guider_ui.screen_fortune_text_label_fotune)) { lv_label_set_text(guider_ui.screen_fortune_text_label_fotune, new_text); } } void show_random_fortune_result(void) { char buffer[128]; srand(lv_tick_get()); snprintf(buffer, sizeof(buffer), "%s", fortunes[rand() % FORTUNE_COUNT]); update_fortune_text(buffer); ui_nav_load_fortune_text_fade(); } // ===== 正念引导状态管理 ===== typedef enum { MINDFULNESS_STOPPED, MINDFULNESS_RUNNING, MINDFULNESS_PAUSED } mindfulness_state_t; typedef struct { uint32_t duration_ms; // 持续时间(毫秒) lv_color_t bg_color; // 背景色 uint8_t arc_direction; // 弧形动画方向:0=顺时针增长, 1=逆时针减少 const char *instruction; // 详细引导语 } mindfulness_phase_t; static const mindfulness_phase_t phases[] = { // 阶段 0: 准备 {3000, LV_COLOR_MAKE(80, 80, 100), 0, "准备:静坐,闭眼,感受呼吸自然流动..."}, // 阶段 1: 吸气(慢) {5000, LV_COLOR_MAKE(30, 80, 150), 0, "吸气:缓慢吸气,感受空气进入腹部..."}, // 阶段 2: 屏息(短) {2000, LV_COLOR_MAKE(50, 100, 180), 0, "屏息:轻轻屏住呼吸,保持平静..."}, // 阶段 3: 呼气(更慢) {6000, LV_COLOR_MAKE(80, 140, 200), 1, "呼气:缓缓呼气,释放所有紧张..."}, // 阶段 4: 放松(停顿) {3000, LV_COLOR_MAKE(60, 110, 170), 0, "放松:完全放松,不控制呼吸..."}, // 阶段 5: 身体扫描(新增!) {4000, LV_COLOR_MAKE(100, 90, 160), 0, "觉察:注意脚趾→膝盖→肩膀→头顶..."}, // 阶段 6: 结束回归 {4000, LV_COLOR_MAKE(120, 120, 140), 0, "结束:轻轻动手指,慢慢睁开眼睛..."} }; #define PHASE_COUNT (sizeof(phases) / sizeof(phases[0])) static mindfulness_state_t mindfulness_state = MINDFULNESS_STOPPED; static lv_timer_t *mindfulness_timer = NULL; static uint8_t current_phase = 0; static uint32_t phase_start_tick = 0; static uint32_t pause_start_timestamp = 0; // 定时器回调:驱动呼吸动画 static void mindfulness_timer_cb(lv_timer_t *timer) { if (mindfulness_state != MINDFULNESS_RUNNING) return; uint32_t now = lv_tick_get(); uint32_t elapsed = now - phase_start_tick; const mindfulness_phase_t *phase = &phases[current_phase]; uint32_t total = phase->duration_ms; // 阶段结束:切换到下一阶段 if (elapsed >= total) { // 检查是否是最后一个阶段 if (current_phase == PHASE_COUNT - 1) { // 周期完成!停止并回到初始状态 mindfulness_state = MINDFULNESS_STOPPED; if (mindfulness_timer) lv_timer_pause(mindfulness_timer); // 更新 UI:显示”完成” lv_label_set_text(guider_ui.screen_mindfulness_label_phase, "正念练习完成,感谢你的专注!"); lv_arc_set_value(guider_ui.screen_mindfulness_arc_breath, 0); lv_obj_set_style_bg_color(guider_ui.screen_mindfulness, lv_color_hex(0x000000), LV_PART_MAIN); // 【解决方案二】清除正念活跃标志,恢复手势切换 g_mindfulness_active = false; ESP_LOGI("mindfulness", "session completed, gesture enabled"); return; // 不再继续 } // 否则进入下一阶段 current_phase++; phase_start_tick = now; phase = &phases[current_phase]; // 更新 UI lv_obj_set_style_bg_color(guider_ui.screen_mindfulness, phase->bg_color, LV_PART_MAIN); lv_label_set_text(guider_ui.screen_mindfulness_label_phase, phase->instruction); } // 更新弧形进度 if (mindfulness_state == MINDFULNESS_RUNNING) { int16_t angle = (elapsed * 360) / total; if (phase->arc_direction == 1) { angle = 360 - angle; // 呼气时反向 } lv_arc_set_value(guider_ui.screen_mindfulness_arc_breath, angle); } } // 开始呼吸正念 static void start_mindfulness_session(void) { if (mindfulness_timer) { lv_timer_del(mindfulness_timer); mindfulness_timer = NULL; } mindfulness_state = MINDFULNESS_RUNNING; current_phase = 0; phase_start_tick = lv_tick_get(); if (!mindfulness_timer) { mindfulness_timer = lv_timer_create(mindfulness_timer_cb, 100, NULL); // 100ms 刷新(优化:降低CPU占用) } else { lv_timer_resume(mindfulness_timer); } // 初始化 UI const mindfulness_phase_t *first = &phases[0]; lv_obj_set_style_bg_color(guider_ui.screen_mindfulness, first->bg_color, LV_PART_MAIN); lv_label_set_text(guider_ui.screen_mindfulness_label_phase, first->instruction); lv_arc_set_value(guider_ui.screen_mindfulness_arc_breath, 0); // 【解决方案二】设置正念活跃标志,禁用手势切换 g_mindfulness_active = true; ESP_LOGI("mindfulness", "start session, gesture disabled"); } // 重置呼吸状态 static void reset_mindfulness_session(void) { mindfulness_state = MINDFULNESS_STOPPED; current_phase = 0; phase_start_tick = 0; if (mindfulness_timer) lv_timer_pause(mindfulness_timer); // 重置 UI 到初始状态 lv_label_set_text(guider_ui.screen_mindfulness_label_phase, "准备开始"); lv_arc_set_value(guider_ui.screen_mindfulness_arc_breath, 0); lv_obj_set_style_bg_color(guider_ui.screen_mindfulness, lv_color_hex(0x000000), LV_PART_MAIN); lv_label_set_text(guider_ui.screen_mindfulness_label_phase, ""); lv_obj_update_layout(guider_ui.screen_mindfulness_label_phase); lv_label_set_text(guider_ui.screen_mindfulness_label_phase, "准备开始"); lv_obj_set_style_bg_color(guider_ui.screen_mindfulness_btn_pause, lv_color_hex(0x29333B), LV_PART_MAIN); lv_label_set_text(guider_ui.screen_mindfulness_btn_pause_label, "暂停/继续"); lv_obj_invalidate(guider_ui.screen_mindfulness); // 【解决方案二】清除正念活跃标志,恢复手势切换 g_mindfulness_active = false; ESP_LOGI("mindfulness", "session reset, gesture enabled"); } static void screen_mindfulness_btn_phase_event_handler (lv_event_t *e) { lv_event_code_t code = lv_event_get_code(e); switch (code) { case LV_EVENT_PRESSED: { if (mindfulness_state == MINDFULNESS_RUNNING) { // 暂停 mindfulness_state = MINDFULNESS_PAUSED; pause_start_timestamp = lv_tick_get(); lv_obj_set_style_bg_color(guider_ui.screen_mindfulness_btn_pause, lv_color_hex(0x4CAF50), LV_PART_MAIN); // 绿色:表示可继续 lv_label_set_text(guider_ui.screen_mindfulness_label_phase, "已暂停"); lv_obj_invalidate(guider_ui.screen_mindfulness); } else if (mindfulness_state == MINDFULNESS_PAUSED) { // =========== 继续逻辑 =========== mindfulness_state = MINDFULNESS_RUNNING; // 1. 时间补偿 (保持不变) uint32_t now = lv_tick_get(); phase_start_tick += (now - pause_start_timestamp); // 2. 恢复按钮颜色 (保持不变) lv_obj_set_style_bg_color(guider_ui.screen_mindfulness_btn_pause, lv_color_hex(0xF44336), LV_PART_MAIN); // 获取当前正在进行的阶段数据 const mindfulness_phase_t *phase = &phases[current_phase]; // 重新设置文字为当前阶段的引导语 (例如 "吸气...") lv_label_set_text(guider_ui.screen_mindfulness_label_phase, phase->instruction); // 强制刷新文字区域,确保“已暂停”被立刻擦除 lv_obj_invalidate(guider_ui.screen_mindfulness); // ========================================================== // 4. 立即更新进度条 (保持不变) if (mindfulness_timer) { mindfulness_timer_cb(mindfulness_timer); } } break; } default: break; } } static void screen_mindfulness_btn_start_event_handler (lv_event_t *e) { lv_event_code_t code = lv_event_get_code(e); switch (code) { case LV_EVENT_PRESSED: { if (mindfulness_state == MINDFULNESS_STOPPED || mindfulness_state == MINDFULNESS_PAUSED) { start_mindfulness_session(); } break; } default: break; } } static void screen_mindfulness_btn_reset_event_handler (lv_event_t *e) { lv_event_code_t code = lv_event_get_code(e); switch (code) { case LV_EVENT_PRESSED: { reset_mindfulness_session(); break; } default: break; } } static void screen_mindfulness_img_mindfulness_back_event_handler (lv_event_t *e) { lv_event_code_t code = lv_event_get_code(e); switch (code) { case LV_EVENT_PRESSED: { if (mindfulness_timer) { lv_timer_del(mindfulness_timer); mindfulness_timer = NULL; } mindfulness_state = MINDFULNESS_STOPPED; // 重置状态 // 【解决方案二】清除正念活跃标志,恢复手势切换 g_mindfulness_active = false; ESP_LOGI("mindfulness", "back to components, gesture enabled"); ui_nav_load_scr_animation(&guider_ui, &guider_ui.screen_component, guider_ui.screen_component_del, &guider_ui.screen_mindfulness_del, setup_scr_screen_component, LV_SCR_LOAD_ANIM_MOVE_LEFT, 150, 0, false, true, UI_IDX_COMPONENTS); break; } default: break; } } void events_init_screen_mindfulness (lv_ui *ui) { lv_obj_add_event_cb(ui->screen_mindfulness_btn_pause, screen_mindfulness_btn_phase_event_handler, LV_EVENT_ALL, ui); lv_obj_add_event_cb(ui->screen_mindfulness_btn_start, screen_mindfulness_btn_start_event_handler, LV_EVENT_ALL, ui); lv_obj_add_event_cb(ui->screen_mindfulness_btn_reset, screen_mindfulness_btn_reset_event_handler, LV_EVENT_ALL, ui); lv_obj_add_event_cb(ui->screen_mindfulness_img_mindfulness_back, screen_mindfulness_img_mindfulness_back_event_handler, LV_EVENT_ALL, ui); // 【解决方案】禁用正念界面的手势切换,防止练习被打断 // 方法:清除手势标志,使该界面不响应手势事件 lv_obj_remove_flag(ui->screen_mindfulness, LV_OBJ_FLAG_GESTURE_BUBBLE); lv_obj_clear_flag(ui->screen_mindfulness, LV_OBJ_FLAG_CLICKABLE); } // ===== 签文数据(按分类)===== typedef struct { const char *category; const char *text; } lottery_item_t; // static const lottery_item_t all_messages[] = { // // ========== 社交 ========== // {"社交", "桃花满屏 · 脱单成功"}, // {"社交", "良缘天降 · 喜结连理"}, // {"社交", "搭子速配 · 知音相伴"}, // {"社交", "颜值开挂 · 桃花爆仓"}, // {"社交", "真爱降临 · 甜度超标"}, // {"社交", "脱单快进 · 缘分拉满"}, // {"社交", "告白必成 · 双向奔赴"}, // {"社交", "桃花朵朵 · 缘分爆棚"}, // // {"社交", "优先处理重要但不紧急的事。"}, // // {"社交", "信任团队,适当放手。"}, // // {"社交", "灵感稍纵即逝,立刻记录。"}, // // {"社交", "客户的真实需求,往往藏在抱怨里。"}, // // {"社交", "慢即是快,稳扎稳打胜过冒进。"}, // // {"社交", "今日贵人运强,多倾听前辈建议。"}, // // {"社交", "完成比完美更重要,先行动起来。"}, // // ========== 治愈 ========== // {"治愈", "摆烂无罪 · 快乐加倍"}, // {"治愈", "烦恼清零 · 快乐加载"}, // {"治愈", "懒觉自由 · 元气满满"}, // {"治愈", "美食秒到 · 干饭无忧"}, // {"治愈", "脱发退散 · 发量暴增"}, // {"治愈", "失眠拜拜 · 一夜好眠"}, // {"治愈", "奶茶自由 · 快乐拉满"}, // {"治愈", "追剧无广 · 爽感拉满"}, // // {"治愈", "原谅不是遗忘,而是放过自己。"}, // // {"治愈", "安全感来自内心的稳定,而非对方的承诺。"}, // // {"治愈", "共同成长,比互相依赖更长久。"}, // // {"治愈", "今天适合写一封手写信给在乎的人。"}, // // {"治愈", "情绪来临时,先深呼吸三次再开口。"}, // // {"治愈", "爱自己,是终身浪漫的开始。"}, // // {"治愈", "真正的亲密,是敢于做真实的自己。"}, // // ========== 健康 ========== // {"健康", "喝一杯温水,唤醒身体机能。"}, // {"健康", "久坐一小时,起身活动五分钟。"}, // {"健康", "今晚早睡半小时,明天精神倍增。"}, // {"健康", "吃七分饱,肠胃轻松一整天。"}, // {"健康", "阳光是最好的维生素,出门走走吧。"}, // {"健康", "深呼吸十次,缓解焦虑与压力。"}, // {"健康", "少看屏幕,多看看远方的绿意。"}, // {"健康", "身体是灵魂的圣殿,请温柔对待。"}, // // {"健康", "运动不必剧烈,坚持才是关键。"}, // // {"健康", "睡前放下烦恼,让心先入睡。"}, // // {"健康", "笑一笑,免疫力悄悄提升。"}, // // {"健康", "饮食清淡,内心也会更清明。"}, // // {"健康", "拉伸三分钟,缓解肩颈僵硬。"}, // // {"健康", "水分充足,思维更清晰。"}, // // {"健康", "健康不是目标,而是每一天的选择。"}, // // ========== 心灵 ========== // {"心灵", "你此刻的呼吸,就是生命的奇迹。"}, // {"心灵", "允许自己休息,这不是懒惰。"}, // {"心灵", "杂念如云,天空始终澄明。"}, // {"心灵", "感恩三件小事,点亮今日心情。"}, // {"心灵", "内在平静,胜过外界喧嚣。"}, // {"心灵", "你不需要讨好所有人。"}, // {"心灵", "当下即是全部,无需追逐未来。"}, // {"心灵", "静坐片刻,找回内心的节奏。"}, // {"心灵", "接纳不完美,才是真正的自由。"}, // {"心灵", "你的存在本身,就有价值。"}, // {"心灵", "放下评判,以好奇之心看世界。"}, // {"心灵", "慈悲始于对自己的温柔。"}, // {"心灵", "简单生活,丰盈内心。"}, // {"心灵", "每一次呼吸,都是重新开始的机会。"}, // {"心灵", "你已走在正确的路上,请相信直觉。"}, // // ========== 通用========== // {"通用", "锦鲤附体 · 好运炸屏"}, // {"通用", "欧气护体 · 万事开挂"}, // {"通用", "水逆退散 · 好运加载"}, // {"通用", "幸运值满 · 心想事成"}, // {"通用", "惊喜盲盒 · 好运爆棚"}, // {"通用", "快乐续费 · 好运无限"}, // {"通用", "好运拉满 · 万事顺遂"}, // {"通用", "喜事临门 · 福气满满"}, // {"通用", "水逆退散 · 时来运转"}, // {"通用", "锦鲤天降 · 惊喜连连"}, // {"通用", "烦恼退散 · 快乐加倍"}, // {"通用", "好运爆棚 · 心想事成"}, // {"通用", "福气满格 · 万事胜意"}, // {"通用", "惊喜不断 · 好运连连"}, // {"通用", "元气满满 · 好运加满"}, // {"通用", "万事亨通 · 好运常驻"}, // {"通用", "钱包鼓鼓 · 财运暴击"}, // {"通用", "躺赢剧本 · 万事开挂"}, // }; // #define ITEM_COUNT (sizeof(all_messages) / sizeof(all_messages[0])) // // ===== 历史记录缓存===== // #define HISTORY_MAX 5 // static char history_texts[HISTORY_MAX][128] = {0}; // static uint8_t history_count = 0; // #define NVS_NAMESPACE "lottery" // #define KEY_LAST_DRAW_TS "last_draw_ts" // uint64_t boot_timestamp_ms = 0; // // 获取当前“绝对”时间戳(ms) // static uint64_t get_current_abs_time_ms(void) { // boot_timestamp_ms = esp_timer_get_time() / 1000; // return boot_timestamp_ms + (esp_timer_get_time() / 1000); // } // // 检查今日是否已抽签 // bool is_drawn_today(void) { // nvs_handle_t handle; // esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READONLY, &handle); // if (err != ESP_OK) return false; // uint64_t last_draw_ts = 0; // err = nvs_get_u64(handle, KEY_LAST_DRAW_TS, &last_draw_ts); // nvs_close(handle); // if (err != ESP_OK) { // return false; // 从未抽过 // } // uint64_t current_ts = get_current_abs_time_ms(); // uint64_t diff_ms = current_ts - last_draw_ts; // // 如果距离上次抽签 < 24 小时(86400000 ms),视为同一天 // return (diff_ms < 24 * 60 * 60 * 1000ULL); // } // // 标记今日已抽签 // void mark_drawn_today(void) { // nvs_handle_t handle; // if (nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle) != ESP_OK) { // return; // } // uint64_t current_ts = get_current_abs_time_ms(); // nvs_set_u64(handle, KEY_LAST_DRAW_TS, current_ts); // nvs_commit(handle); // nvs_close(handle); // } // // ===== 添加历史记录 ===== // static void add_to_history(const char *msg) { // // 移动旧记录 // if (history_count >= HISTORY_MAX) { // for (int i = 0; i < HISTORY_MAX - 1; i++) { // strcpy(history_texts[i], history_texts[i + 1]); // } // history_count = HISTORY_MAX - 1; // } // strncpy(history_texts[history_count], msg, sizeof(history_texts[0]) - 1); // history_count++; // } static const char *CATEGORIES[] = {"通用", "社交", "治愈", "健康", "心灵"}; #define CAT_COUNT (sizeof(CATEGORIES)/sizeof(CATEGORIES[0])) // ===== 抽签核心逻辑 ===== // static void perform_draw(void) // { // // if (is_drawn_today()) { // // lv_label_set_text(guider_ui.screen_sortition_label_message, "今日已抽过签啦~"); // // return; // // } // // 获取当前选中分类 // uint16_t idx = lv_dropdown_get_selected(guider_ui.screen_sortition_ddlist_category); // if (idx >= CAT_COUNT) idx = 0; // const char *selected = CATEGORIES[idx]; // if (!selected) selected = "通用"; // // 过滤签文 // const lottery_item_t *candidates[ITEM_COUNT]; // uint8_t cand_count = 0; // for (int i = 0; i < ITEM_COUNT; i++) { // if (strcmp(all_messages[i].category, "通用") == 0 || // strcmp(all_messages[i].category, selected) == 0) { // candidates[cand_count++] = &all_messages[i]; // } // } // if (cand_count == 0) { // lv_label_set_text(guider_ui.screen_sortition_label_message, "暂无此分类签文"); // return; // } // // 随机抽取 // static bool seeded = false; // if (!seeded) { // srand((unsigned int)lv_tick_get()); // seeded = true; // } // const lottery_item_t *chosen = candidates[rand() % cand_count]; // // 显示 + 存储 // lv_label_set_text(guider_ui.screen_sortition_label_message, chosen->text); // // add_to_history(chosen->text); // // mark_drawn_today(); // } static void screen_sortition_btn_draw_event_handler (lv_event_t *e) { lv_event_code_t code = lv_event_get_code(e); switch (code) { case LV_EVENT_PRESSED: { perform_draw(); break; } default: break; } } static void screen_sortition_img_sortition_back_event_handler (lv_event_t *e) { lv_event_code_t code = lv_event_get_code(e); switch (code) { case LV_EVENT_PRESSED: { ui_nav_load_scr_animation(&guider_ui, &guider_ui.screen_component, guider_ui.screen_component_del, &guider_ui.screen_sortition_del, setup_scr_screen_component, LV_SCR_LOAD_ANIM_MOVE_LEFT, 150, 0, false, true, UI_IDX_COMPONENTS); break; } default: break; } } void events_init_screen_sortition (lv_ui *ui) { lv_obj_add_event_cb(ui->screen_sortition_btn_draw, screen_sortition_btn_draw_event_handler, LV_EVENT_ALL, ui); lv_obj_add_event_cb(ui->screen_sortition_img_sortition_back, screen_sortition_img_sortition_back_event_handler, LV_EVENT_ALL, ui); lv_obj_add_event_cb(ui->screen_sortition_btn_history, screen_sortition_btn_history_event_handler, LV_EVENT_ALL, ui); } static void screen_sortition_btn_history_event_handler (lv_event_t *e) { lv_event_code_t code = lv_event_get_code(e); switch (code) { case LV_EVENT_PRESSED: { // // 清空历史列表 // lv_obj_clean(guider_ui.screen_sortition_history_list_history); // // 添加历史项 // for (int i = history_count - 1; i >= 0; i--) { // lv_obj_t *item = lv_list_add_text(guider_ui.screen_sortition_history_list_history, history_texts[i]); // lv_obj_set_style_pad_left(item, 10, LV_PART_MAIN); // } /* 抽签历史页 setup 已移除:此处不再切换屏幕 */ break; } default: break; } } static void screen_sortition_history_img_sortition_history_back_event_handler (lv_event_t *e) { lv_event_code_t code = lv_event_get_code(e); switch (code) { case LV_EVENT_PRESSED: { /* 抽签页 setup 已移除:此处不再切换屏幕 */ break; } default: break; } } void events_init_screen_sortition_history (lv_ui *ui) { lv_obj_add_event_cb(ui->screen_sortition_history_img_sortition_history_back, screen_sortition_history_img_sortition_history_back_event_handler, LV_EVENT_ALL, ui); } void events_init_screen_fortune_text(lv_ui *ui) { lv_obj_add_event_cb(ui->screen_fortune_text_btn_2, sortition_text_back_to_components, LV_EVENT_PRESSED, NULL); } static void sortition_text_back_to_components(lv_event_t *e) { (void)e; extern bool back2default; back2default = true; // 返回时恢复默认表情 ui_nav_go_by_index(UI_IDX_COMPONENTS, LV_SCR_LOAD_ANIM_FADE_ON); } static void sortition_text_pressed_handler(lv_event_t *e) { sortition_text_back_to_components(e); } void events_init_screen_sortition_text(lv_ui *ui) { lv_obj_add_event_cb(ui->screen_sortition_text_btn_st_back, sortition_text_back_to_components, LV_EVENT_PRESSED, NULL); } static void screen_Perpetual_Calendar_btn_PC_back_event_handler(lv_event_t *e) { lv_event_code_t code = lv_event_get_code(e); switch (code) { case LV_EVENT_PRESSED: /* 与抽签页返回一致:经 ui_load_scr_animation 正确恢复可能已被 auto_del 的 screen_component */ ui_nav_load_scr_animation(&guider_ui, &guider_ui.screen_component, guider_ui.screen_component_del, &guider_ui.screen_Perpetual_Calendar_del, setup_scr_screen_component, LV_SCR_LOAD_ANIM_MOVE_LEFT, 150, 0, false, true, UI_IDX_COMPONENTS); break; default: break; } } void events_init_screen_Perpetual_Calendar(lv_ui *ui) { lv_obj_add_event_cb(ui->screen_Perpetual_Calendar_btn_PC_back, screen_Perpetual_Calendar_btn_PC_back_event_handler, LV_EVENT_ALL, ui); } void events_init(lv_ui *ui) { }