Files
P4-USB-transfer/main/ui/generated/events_init.c
T

1966 lines
78 KiB
C
Raw Normal View History

2026-10-13 21:54:50 +08:00
/*
* 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 <stdio.h>
#include <inttypes.h>
#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)
{
}