/** **************************************************************************************************** * @file ui.c * @author HiFi Player Project * @version V1.2 * @date 2026-01-01 * @brief 界面路由器:模式切换 + 按键事件分发 * * --------------------------------------------------------------------------------------------------- * 全局按键约定(K0 为“返回/切换”键,任何 App 都遵循同一套语义) * --------------------------------------------------------------------------------------------------- * K0 短按 在任意 App 内 -> 返回主界面 * 在主界面 -> 切换到下一个应用 * K0 长按 任意界面 -> 直接切换到下一个应用 * K1/K2/K3 由当前 App 自己定义(见各 App 界面底部的按键提示) * * 因此新增 App 时,只需实现自己的 K1/K2/K3,返回与切换是完全自动的。 **************************************************************************************************** */ #include "ui.h" #include "ui_common.h" #include "app.h" #include "ui_home.h" #include "ui_player.h" #include "ui_files.h" static app_mode_t s_last_mode = APP_MODE_NUM; /** * @brief 切换到某个 App 的界面 */ static void ui_load_mode(app_mode_t mode) { switch (mode) { case APP_MODE_PLAYER: ui_player_show(); break; case APP_MODE_FILES: ui_files_show(); break; case APP_MODE_HOME: default: ui_home_show(); break; } } /** * @brief 把按键事件分发给当前界面 */ static void ui_dispatch(const app_event_t *ev) { app_mode_t mode = app_get_mode(); /* ---------------- 1. K0:返回 / 切换应用(全局语义) ---------------- */ if (ev->key == APP_KEY_0) { /* 高亮交给当前界面,纯视觉效果 */ switch (mode) { case APP_MODE_PLAYER: ui_player_flash(ev->key); break; case APP_MODE_FILES: ui_files_flash(ev->key); break; case APP_MODE_HOME: default: ui_home_flash(ev->key); break; } if (ev->type == APP_KEY_LONG) { app_switch_next(); /* 长按:直接跳到下一个应用 */ } else if (mode == APP_MODE_HOME) { app_switch_next(); /* 主界面短按:切换应用 */ } else if ((mode == APP_MODE_FILES) && ui_files_back()) { /* 文件管理器:先返回上一级目录,停留在本应用内 */ } else { app_set_mode(APP_MODE_HOME); /* App 内短按:返回主界面 */ } return; } /* ---------------- 2. 其它按键的长按暂时没有功能 ---------------- */ if (ev->type == APP_KEY_LONG) { return; } /* ---------------- 3. 交给当前 App ---------------- */ switch (mode) { case APP_MODE_PLAYER: ui_player_handle_key(ev->key); break; case APP_MODE_FILES: ui_files_handle_key(ev->key); break; case APP_MODE_HOME: default: ui_home_handle_key(ev->key); break; } } /** * @brief 界面周期任务(在 LVGL 任务中执行) */ static void ui_tick_cb(lv_timer_t *timer) { (void)timer; app_mode_t mode = app_get_mode(); /* ---------------- 1. 应用切换:换屏 ---------------- */ if (mode != s_last_mode) { s_last_mode = mode; ui_load_mode(mode); } /* ---------------- 2. 消费按键事件(唯一消费者) ---------------- */ app_event_t ev; while (app_event_poll(&ev)) { ui_dispatch(&ev); } /* ---------------- 3. 当前界面刷新 ---------------- */ switch (app_get_mode()) { case APP_MODE_PLAYER: ui_player_tick(); break; case APP_MODE_FILES: ui_files_tick(); break; case APP_MODE_HOME: default: ui_home_tick(); break; } } void ui_create(void) { ui_home_create(); ui_player_create(); ui_files_create(); s_last_mode = app_get_mode(); ui_load_mode(s_last_mode); lv_timer_create(ui_tick_cb, UI_TICK_MS, NULL); }