Files
blinky/src/ui/ui_main.c
skiinder 48968e7880 feat(ui): 重构设置界面为页面控制器架构
- 添加新的UI页面基础架构(ui_page.h),包含页面操作接口
- 创建设置页面控制器(ui_settings_controller.h/.c)来管理页面导航
- 实现具体的设置页面类型:根页面、BLE页面、主题页面
- 修改display_module.c以使用新的页面系统替代旧的状态机
- 移除过时的settings_ui.h头文件和相关状态结构
- 更新事件处理逻辑以使用页面指针而非状态数据传递
- 修改主界面实现以适配统一的页面接口标准
2026-04-23 18:46:55 +08:00

355 lines
9.5 KiB
C

#include <string.h>
#include <lvgl.h>
#include <zephyr/sys/printk.h>
#include "ui_main.h"
enum ui_status_id {
UI_STATUS_USB = 0,
UI_STATUS_BLE,
UI_STATUS_NUMLOCK,
UI_STATUS_CAPSLOCK,
UI_STATUS_COUNT,
};
enum {
UI_LED_MASK_NUM_LOCK = BIT(0),
UI_LED_MASK_CAPS_LOCK = BIT(1),
};
struct ui_main_ctx {
lv_obj_t *content;
lv_obj_t *status_badges[UI_STATUS_COUNT];
lv_obj_t *status_labels[UI_STATUS_COUNT];
lv_obj_t *battery_icon;
lv_obj_t *battery_label;
lv_obj_t *battery_state_label;
lv_obj_t *date_label;
lv_obj_t *time_label;
};
static struct ui_main_ctx g_ui;
static bool ui_initialized;
static const struct ui_main_model *page_model;
static const char *page_date_text;
static const char *page_time_text;
static const char *const status_texts[UI_STATUS_COUNT] = {
LV_SYMBOL_USB,
LV_SYMBOL_BLUETOOTH,
"1",
"A",
};
static lv_color_t ui_main_get_battery_color(uint8_t battery_level)
{
if (battery_level > 70U) {
return lv_color_hex(0x8BD450);
}
if (battery_level >= 20U) {
return lv_color_hex(0xF4D35E);
}
return lv_color_hex(0xE63946);
}
static const char *ui_main_get_battery_symbol(uint8_t battery_level)
{
if (battery_level > 85U) {
return LV_SYMBOL_BATTERY_FULL;
}
if (battery_level > 60U) {
return LV_SYMBOL_BATTERY_3;
}
if (battery_level > 35U) {
return LV_SYMBOL_BATTERY_2;
}
if (battery_level >= 20U) {
return LV_SYMBOL_BATTERY_1;
}
return LV_SYMBOL_BATTERY_EMPTY;
}
static bool ui_main_status_is_active(enum ui_status_id id,
const struct ui_main_model *model)
{
switch (id) {
case UI_STATUS_USB:
return model->mode == MODE_SWITCH_USB;
case UI_STATUS_BLE:
return model->mode == MODE_SWITCH_BLE;
case UI_STATUS_NUMLOCK:
return (model->led_mask & UI_LED_MASK_NUM_LOCK) != 0U;
case UI_STATUS_CAPSLOCK:
return (model->led_mask & UI_LED_MASK_CAPS_LOCK) != 0U;
default:
return false;
}
}
static void ui_main_create_status_chip(lv_obj_t *parent, enum ui_status_id id)
{
lv_obj_t *badge = lv_obj_create(parent);
lv_obj_t *label = lv_label_create(badge);
lv_obj_remove_style_all(badge);
lv_obj_set_size(badge, 50, 32);
lv_obj_set_style_radius(badge, 10, 0);
lv_obj_set_style_bg_opa(badge, LV_OPA_COVER, 0);
lv_obj_set_style_pad_all(badge, 0, 0);
lv_label_set_text(label, status_texts[id]);
lv_obj_set_width(label, LV_PCT(100));
lv_obj_set_style_text_font(label, &lv_font_montserrat_14, 0);
lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_CENTER, 0);
lv_obj_center(label);
g_ui.status_badges[id] = badge;
g_ui.status_labels[id] = label;
}
void ui_main_refresh_status_bar(const struct ui_main_model *model)
{
if (!ui_initialized) {
return;
}
for (uint32_t i = 0; i < UI_STATUS_COUNT; i++) {
lv_obj_t *badge = g_ui.status_badges[i];
lv_obj_t *label = g_ui.status_labels[i];
bool active = ui_main_status_is_active((enum ui_status_id)i, model);
if ((badge == NULL) || (label == NULL)) {
continue;
}
lv_obj_set_style_border_width(badge, 3, 0);
lv_obj_set_style_border_color(
badge,
active ? model->theme_color : model->inactive_border_color, 0);
lv_obj_set_style_bg_color(
badge,
active ? lv_color_hex(0x1D2735) : lv_color_hex(0x161A20), 0);
lv_obj_set_style_text_color(
label,
active ? lv_color_white() : lv_color_hex(0x7C8798), 0);
}
}
void ui_main_refresh_battery(const struct ui_main_model *model)
{
char battery_text[8];
const char *state_symbol = "";
lv_color_t battery_color;
lv_color_t state_color = lv_color_white();
if (!ui_initialized ||
(g_ui.battery_icon == NULL) || (g_ui.battery_label == NULL) ||
(g_ui.battery_state_label == NULL)) {
return;
}
battery_color = ui_main_get_battery_color(model->battery_level);
snprintk(battery_text, sizeof(battery_text), "%u%%", model->battery_level);
if (model->full) {
state_symbol = LV_SYMBOL_USB;
state_color = lv_color_hex(0x4C9EF5);
} else if (model->charging) {
state_symbol = LV_SYMBOL_CHARGE;
state_color = lv_color_hex(0xF4D35E);
}
lv_label_set_text(g_ui.battery_icon,
ui_main_get_battery_symbol(model->battery_level));
lv_obj_set_style_text_color(g_ui.battery_icon, battery_color, 0);
lv_label_set_text(g_ui.battery_label, battery_text);
lv_label_set_text(g_ui.battery_state_label, state_symbol);
lv_obj_set_style_text_color(g_ui.battery_state_label, state_color, 0);
}
void ui_main_refresh_datetime(const char *date_text, const char *time_text)
{
if (!ui_initialized ||
(g_ui.date_label == NULL) || (g_ui.time_label == NULL)) {
return;
}
lv_label_set_text(g_ui.date_label, date_text);
lv_label_set_text(g_ui.time_label, time_text);
}
void ui_main_refresh_all(const struct ui_main_model *model,
const char *date_text,
const char *time_text)
{
ui_main_refresh_status_bar(model);
ui_main_refresh_battery(model);
ui_main_refresh_datetime(date_text, time_text);
}
void ui_main_init(const struct ui_main_model *model,
const char *date_text,
const char *time_text)
{
lv_obj_t *screen = lv_screen_active();
lv_obj_t *content;
lv_obj_t *top_row;
lv_obj_t *battery_wrap;
lv_obj_t *middle_row;
lv_obj_t *bottom_row;
if (ui_initialized) {
ui_main_refresh_all(model, date_text, time_text);
return;
}
memset(&g_ui, 0, sizeof(g_ui));
lv_obj_clean(screen);
lv_obj_set_style_bg_color(screen, lv_color_hex(0x0F1115), 0);
lv_obj_set_style_bg_opa(screen, LV_OPA_COVER, 0);
lv_obj_set_style_text_color(screen, lv_color_white(), 0);
lv_obj_set_style_pad_all(screen, 0, 0);
lv_obj_set_scrollbar_mode(screen, LV_SCROLLBAR_MODE_OFF);
content = lv_obj_create(screen);
g_ui.content = content;
lv_obj_remove_style_all(content);
lv_obj_set_size(content, LV_PCT(100), LV_PCT(100));
lv_obj_set_style_bg_opa(content, LV_OPA_TRANSP, 0);
lv_obj_set_style_pad_left(content, 14, 0);
lv_obj_set_style_pad_right(content, 14, 0);
lv_obj_set_style_pad_top(content, 8, 0);
lv_obj_set_style_pad_bottom(content, 8, 0);
lv_obj_set_layout(content, LV_LAYOUT_FLEX);
lv_obj_set_flex_flow(content, LV_FLEX_FLOW_COLUMN);
lv_obj_set_flex_align(content, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER,
LV_FLEX_ALIGN_CENTER);
top_row = lv_obj_create(content);
lv_obj_remove_style_all(top_row);
lv_obj_set_width(top_row, LV_PCT(100));
lv_obj_set_flex_grow(top_row, 1);
lv_obj_set_style_bg_opa(top_row, LV_OPA_TRANSP, 0);
lv_obj_set_layout(top_row, LV_LAYOUT_FLEX);
lv_obj_set_flex_flow(top_row, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(top_row, LV_FLEX_ALIGN_SPACE_BETWEEN,
LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
g_ui.date_label = lv_label_create(top_row);
lv_obj_set_style_text_font(g_ui.date_label, &lv_font_montserrat_14, 0);
lv_obj_set_style_text_color(g_ui.date_label, lv_color_hex(0xD8DEE9), 0);
battery_wrap = lv_obj_create(top_row);
lv_obj_remove_style_all(battery_wrap);
lv_obj_set_width(battery_wrap, LV_SIZE_CONTENT);
lv_obj_set_layout(battery_wrap, LV_LAYOUT_FLEX);
lv_obj_set_flex_flow(battery_wrap, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(battery_wrap, LV_FLEX_ALIGN_CENTER,
LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
lv_obj_set_style_pad_column(battery_wrap, 4, 0);
g_ui.battery_icon = lv_label_create(battery_wrap);
lv_obj_set_style_text_font(g_ui.battery_icon, &lv_font_montserrat_14, 0);
g_ui.battery_label = lv_label_create(battery_wrap);
lv_obj_set_style_text_font(g_ui.battery_label, &lv_font_montserrat_14, 0);
lv_obj_set_style_text_color(g_ui.battery_label, lv_color_hex(0xD8DEE9), 0);
g_ui.battery_state_label = lv_label_create(battery_wrap);
lv_obj_set_style_text_font(g_ui.battery_state_label, &lv_font_montserrat_14, 0);
middle_row = lv_obj_create(content);
lv_obj_remove_style_all(middle_row);
lv_obj_set_width(middle_row, LV_PCT(100));
lv_obj_set_flex_grow(middle_row, 2);
lv_obj_set_style_bg_opa(middle_row, LV_OPA_TRANSP, 0);
g_ui.time_label = lv_label_create(middle_row);
lv_obj_set_style_text_font(g_ui.time_label, &lv_font_montserrat_32, 0);
lv_obj_set_style_text_color(g_ui.time_label, lv_color_white(), 0);
lv_obj_center(g_ui.time_label);
bottom_row = lv_obj_create(content);
lv_obj_remove_style_all(bottom_row);
lv_obj_set_width(bottom_row, LV_PCT(100));
lv_obj_set_flex_grow(bottom_row, 1);
lv_obj_set_style_bg_opa(bottom_row, LV_OPA_TRANSP, 0);
lv_obj_set_layout(bottom_row, LV_LAYOUT_FLEX);
lv_obj_set_flex_flow(bottom_row, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(bottom_row, LV_FLEX_ALIGN_CENTER,
LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
lv_obj_set_style_pad_column(bottom_row, 6, 0);
for (uint32_t i = 0; i < UI_STATUS_COUNT; i++) {
ui_main_create_status_chip(bottom_row, (enum ui_status_id)i);
}
ui_main_refresh_all(model, date_text, time_text);
ui_initialized = true;
}
void ui_main_deinit(void)
{
if (!ui_initialized) {
return;
}
if (g_ui.content != NULL) {
lv_obj_delete(g_ui.content);
}
memset(&g_ui, 0, sizeof(g_ui));
ui_initialized = false;
}
static void main_page_init(struct ui_page *page)
{
ARG_UNUSED(page);
ui_main_init(page_model, page_date_text, page_time_text);
}
static void main_page_deinit(struct ui_page *page)
{
ARG_UNUSED(page);
ui_main_deinit();
}
static void main_page_refresh(struct ui_page *page)
{
ARG_UNUSED(page);
ui_main_refresh_all(page_model, page_date_text, page_time_text);
}
static const struct ui_page_ops main_page_ops = {
.init = main_page_init,
.deinit = main_page_deinit,
.refresh = main_page_refresh,
};
static struct ui_page main_page = {
.ops = &main_page_ops,
};
struct ui_page *ui_main_page_get(const struct ui_main_model *model,
const char *date_text,
const char *time_text)
{
page_model = model;
page_date_text = date_text;
page_time_text = time_text;
return &main_page;
}