refactor(display): 重构显示模块UI逻辑分离到独立组件
- 将UI相关的代码从display_module.c中提取到新的display_ui.c文件 - 创建display_ui.h头文件定义UI模型和接口函数 - 在CMakeLists.txt中添加UI目录包含路径和源文件引用 - 修改display_module.c中的UI相关数据结构和函数调用 - 将UI创建和刷新逻辑替换为对新UI模块的调用 - 优化了时间日期文本的更新机制,提高性能 - 移除了原有的内部UI实现代码,保持模块职责清晰
This commit is contained in:
@@ -10,6 +10,7 @@ project(new_kbd)
|
|||||||
|
|
||||||
zephyr_include_directories(${CMAKE_CURRENT_SOURCE_DIR}/inc)
|
zephyr_include_directories(${CMAKE_CURRENT_SOURCE_DIR}/inc)
|
||||||
zephyr_include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src/events)
|
zephyr_include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src/events)
|
||||||
|
zephyr_include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src/ui)
|
||||||
|
|
||||||
zephyr_compile_definitions(
|
zephyr_compile_definitions(
|
||||||
LV_LVGL_H_INCLUDE_SIMPLE=1
|
LV_LVGL_H_INCLUDE_SIMPLE=1
|
||||||
@@ -56,6 +57,7 @@ target_sources(app PRIVATE
|
|||||||
src/modules/time_manager_module.c
|
src/modules/time_manager_module.c
|
||||||
src/modules/usb_hid_module.c
|
src/modules/usb_hid_module.c
|
||||||
src/modules/ble_hid_module.c
|
src/modules/ble_hid_module.c
|
||||||
|
src/ui/display_ui.c
|
||||||
src/ui/fonts/ui_font_keyboard_small_18.c
|
src/ui/fonts/ui_font_keyboard_small_18.c
|
||||||
src/ui/fonts/ui_font_keyboard_time_48.c
|
src/ui/fonts/ui_font_keyboard_time_48.c
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <string.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
#include <zephyr/device.h>
|
#include <zephyr/device.h>
|
||||||
@@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
#include "battery_status_event.h"
|
#include "battery_status_event.h"
|
||||||
#include "display_theme_event.h"
|
#include "display_theme_event.h"
|
||||||
|
#include "display_ui.h"
|
||||||
#include "keyboard_led_event.h"
|
#include "keyboard_led_event.h"
|
||||||
#include "mode_event.h"
|
#include "mode_event.h"
|
||||||
#include "time_manager.h"
|
#include "time_manager.h"
|
||||||
@@ -39,19 +40,6 @@ LOG_MODULE_REGISTER(MODULE, LOG_LEVEL_INF);
|
|||||||
#define DISPLAY_DEMO_BASE_HOUR 14
|
#define DISPLAY_DEMO_BASE_HOUR 14
|
||||||
#define DISPLAY_DEMO_BASE_MIN 28
|
#define DISPLAY_DEMO_BASE_MIN 28
|
||||||
#define DISPLAY_DEMO_BASE_SEC 36
|
#define DISPLAY_DEMO_BASE_SEC 36
|
||||||
#define DISPLAY_SYMBOL_PLUG "\xEF\x87\xA6" /* U+F1E6, custom plug glyph in ui_font_keyboard_small_18 */
|
|
||||||
|
|
||||||
LV_FONT_DECLARE(ui_font_keyboard_small_18);
|
|
||||||
LV_FONT_DECLARE(ui_font_keyboard_time_48);
|
|
||||||
|
|
||||||
enum display_status_id
|
|
||||||
{
|
|
||||||
DISPLAY_STATUS_USB = 0,
|
|
||||||
DISPLAY_STATUS_BLE,
|
|
||||||
DISPLAY_STATUS_NUMLOCK,
|
|
||||||
DISPLAY_STATUS_CAPSLOCK,
|
|
||||||
DISPLAY_STATUS_COUNT,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum display_pm_state
|
enum display_pm_state
|
||||||
{
|
{
|
||||||
@@ -59,39 +47,6 @@ enum display_pm_state
|
|||||||
DISPLAY_PM_STATE_OFF,
|
DISPLAY_PM_STATE_OFF,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct display_ui_state
|
|
||||||
{
|
|
||||||
lv_color_t theme_color;
|
|
||||||
lv_color_t inactive_border_color;
|
|
||||||
uint8_t battery_level;
|
|
||||||
mode_type_t mode;
|
|
||||||
uint8_t led_mask;
|
|
||||||
uint8_t battery_flags;
|
|
||||||
bool status_enabled[DISPLAY_STATUS_COUNT];
|
|
||||||
lv_obj_t *status_badges[DISPLAY_STATUS_COUNT];
|
|
||||||
lv_obj_t *status_labels[DISPLAY_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;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct display_ctx
|
|
||||||
{
|
|
||||||
const struct device *dev;
|
|
||||||
struct display_capabilities caps;
|
|
||||||
struct k_work_delayable update_work;
|
|
||||||
struct k_work_delayable idle_work;
|
|
||||||
struct k_work_delayable theme_save_work;
|
|
||||||
struct display_ui_state ui;
|
|
||||||
uint32_t tick_count;
|
|
||||||
enum display_pm_state pm_state;
|
|
||||||
bool initialized;
|
|
||||||
bool theme_storage_dirty;
|
|
||||||
bool theme_storage_loaded;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct display_theme_storage {
|
struct display_theme_storage {
|
||||||
uint8_t red;
|
uint8_t red;
|
||||||
uint8_t green;
|
uint8_t green;
|
||||||
@@ -99,6 +54,23 @@ struct display_theme_storage {
|
|||||||
uint8_t valid_marker;
|
uint8_t valid_marker;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct display_ctx
|
||||||
|
{
|
||||||
|
const struct device *dev;
|
||||||
|
struct display_capabilities caps;
|
||||||
|
struct k_work_delayable update_work;
|
||||||
|
struct k_work_delayable idle_work;
|
||||||
|
struct k_work_delayable theme_save_work;
|
||||||
|
struct display_ui_model ui;
|
||||||
|
uint32_t tick_count;
|
||||||
|
enum display_pm_state pm_state;
|
||||||
|
bool initialized;
|
||||||
|
bool theme_storage_dirty;
|
||||||
|
bool theme_storage_loaded;
|
||||||
|
char date_text[16];
|
||||||
|
char time_text[16];
|
||||||
|
};
|
||||||
|
|
||||||
static struct display_ctx disp = {
|
static struct display_ctx disp = {
|
||||||
.dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_display)),
|
.dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_display)),
|
||||||
.ui.theme_color = LV_COLOR_MAKE(0x4C, 0xC9, 0xF0),
|
.ui.theme_color = LV_COLOR_MAKE(0x4C, 0xC9, 0xF0),
|
||||||
@@ -106,7 +78,7 @@ static struct display_ctx disp = {
|
|||||||
.ui.battery_level = 15U,
|
.ui.battery_level = 15U,
|
||||||
.ui.battery_flags = 0U,
|
.ui.battery_flags = 0U,
|
||||||
.ui.mode = MODE_TYPE_USB,
|
.ui.mode = MODE_TYPE_USB,
|
||||||
.ui.status_enabled = {true, true, false, true},
|
.ui.led_mask = 0U,
|
||||||
.pm_state = DISPLAY_PM_STATE_OFF,
|
.pm_state = DISPLAY_PM_STATE_OFF,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -115,15 +87,6 @@ static struct display_theme_storage display_theme_storage;
|
|||||||
static const struct led_dt_spec display_backlight =
|
static const struct led_dt_spec display_backlight =
|
||||||
LED_DT_SPEC_GET(DT_NODELABEL(backlight));
|
LED_DT_SPEC_GET(DT_NODELABEL(backlight));
|
||||||
|
|
||||||
static const char *const g_status_texts[DISPLAY_STATUS_COUNT] = {
|
|
||||||
LV_SYMBOL_USB,
|
|
||||||
LV_SYMBOL_BLUETOOTH,
|
|
||||||
"1",
|
|
||||||
"A",
|
|
||||||
};
|
|
||||||
|
|
||||||
static void display_refresh_all_locked(void);
|
|
||||||
|
|
||||||
static int display_theme_store(const struct display_theme_storage *storage)
|
static int display_theme_store(const struct display_theme_storage *storage)
|
||||||
{
|
{
|
||||||
char key[] = MODULE_NAME "/" DISPLAY_THEME_STORAGE_KEY;
|
char key[] = MODULE_NAME "/" DISPLAY_THEME_STORAGE_KEY;
|
||||||
@@ -140,9 +103,9 @@ static int display_theme_store(const struct display_theme_storage *storage)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void display_theme_set_rgb(uint8_t red,
|
static void display_theme_set_rgb(uint8_t red,
|
||||||
uint8_t green,
|
uint8_t green,
|
||||||
uint8_t blue,
|
uint8_t blue,
|
||||||
bool persist)
|
bool persist)
|
||||||
{
|
{
|
||||||
disp.ui.theme_color = lv_color_make(red, green, blue);
|
disp.ui.theme_color = lv_color_make(red, green, blue);
|
||||||
|
|
||||||
@@ -226,7 +189,6 @@ static void display_schedule_idle_timeout(k_timeout_t delay)
|
|||||||
k_work_reschedule(&disp.idle_work, delay);
|
k_work_reschedule(&disp.idle_work, delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 背光初始化独立处理,避免 UI 创建逻辑里混入硬件使能细节。 */
|
|
||||||
static int display_backlight_set(uint8_t brightness)
|
static int display_backlight_set(uint8_t brightness)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
@@ -252,7 +214,59 @@ static bool display_is_active(void)
|
|||||||
return disp.pm_state == DISPLAY_PM_STATE_ACTIVE;
|
return disp.pm_state == DISPLAY_PM_STATE_ACTIVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 只负责保活屏幕空闲计时,不隐式点亮屏幕。 */
|
static void display_update_datetime_text(void)
|
||||||
|
{
|
||||||
|
struct time_manager_snapshot snapshot;
|
||||||
|
int err = time_manager_get_snapshot(&snapshot);
|
||||||
|
|
||||||
|
if (!err)
|
||||||
|
{
|
||||||
|
time_t local_seconds;
|
||||||
|
struct tm tm_buf;
|
||||||
|
struct tm *tm_info;
|
||||||
|
|
||||||
|
local_seconds = (time_t)(snapshot.utc_ms / 1000ULL) +
|
||||||
|
(time_t)((int32_t)snapshot.timezone_min * 60);
|
||||||
|
tm_info = gmtime_r(&local_seconds, &tm_buf);
|
||||||
|
|
||||||
|
if (tm_info)
|
||||||
|
{
|
||||||
|
unsigned int year = (unsigned int)(tm_info->tm_year + 1900);
|
||||||
|
unsigned int month = (unsigned int)(tm_info->tm_mon + 1);
|
||||||
|
unsigned int day = (unsigned int)tm_info->tm_mday;
|
||||||
|
unsigned int hour = (unsigned int)tm_info->tm_hour;
|
||||||
|
unsigned int minute = (unsigned int)tm_info->tm_min;
|
||||||
|
unsigned int second = (unsigned int)tm_info->tm_sec;
|
||||||
|
|
||||||
|
snprintk(disp.date_text, sizeof(disp.date_text), "%04u/%02u/%02u",
|
||||||
|
year, month, day);
|
||||||
|
snprintk(disp.time_text, sizeof(disp.time_text), "%02u:%02u:%02u",
|
||||||
|
hour, minute, second);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
uint32_t seconds = disp.tick_count;
|
||||||
|
uint32_t hour = (DISPLAY_DEMO_BASE_HOUR + (seconds / 3600U)) % 24U;
|
||||||
|
uint32_t minute = (DISPLAY_DEMO_BASE_MIN + ((seconds / 60U) % 60U)) % 60U;
|
||||||
|
uint32_t second = (DISPLAY_DEMO_BASE_SEC + (seconds % 60U)) % 60U;
|
||||||
|
|
||||||
|
snprintk(disp.date_text, sizeof(disp.date_text), "%04d/%02d/%02d",
|
||||||
|
DISPLAY_DEMO_BASE_YEAR,
|
||||||
|
DISPLAY_DEMO_BASE_MONTH,
|
||||||
|
DISPLAY_DEMO_BASE_DAY);
|
||||||
|
snprintk(disp.time_text, sizeof(disp.time_text), "%02u:%02u:%02u",
|
||||||
|
hour, minute, second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void display_refresh_all_locked(void)
|
||||||
|
{
|
||||||
|
display_update_datetime_text();
|
||||||
|
display_ui_refresh_all(&disp.ui, disp.date_text, disp.time_text);
|
||||||
|
}
|
||||||
|
|
||||||
static void display_kick_idle_timer(void)
|
static void display_kick_idle_timer(void)
|
||||||
{
|
{
|
||||||
if (!disp.initialized || !display_is_active())
|
if (!disp.initialized || !display_is_active())
|
||||||
@@ -261,7 +275,6 @@ static void display_kick_idle_timer(void)
|
|||||||
display_schedule_idle_timeout(K_MINUTES(DISPLAY_IDLE_TIMEOUT_MIN));
|
display_schedule_idle_timeout(K_MINUTES(DISPLAY_IDLE_TIMEOUT_MIN));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 熄屏时同时关闭刷新和背光,并将模块状态切到 OFF。 */
|
|
||||||
static void display_sleep(void)
|
static void display_sleep(void)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
@@ -287,7 +300,6 @@ static void display_sleep(void)
|
|||||||
module_set_state(MODULE_STATE_OFF);
|
module_set_state(MODULE_STATE_OFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 唤醒屏幕后立刻刷新 UI,并重新启动定时刷新和空闲超时。 */
|
|
||||||
static void display_wake(void)
|
static void display_wake(void)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
@@ -323,309 +335,6 @@ static void display_idle_timeout_fn(struct k_work *work)
|
|||||||
display_sleep();
|
display_sleep();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 电量颜色与 PC 原型保持一致,顶部状态区能快速表达健康度。 */
|
|
||||||
static lv_color_t display_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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 电池图标由精简图标字体提供,不再依赖 LVGL 内建字体资源。 */
|
|
||||||
static const char *display_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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 模式事件只需要驱动 USB/BLE 两个 badge,2.4G 模式两者都灭。 */
|
|
||||||
static void display_update_mode_state(mode_type_t mode)
|
|
||||||
{
|
|
||||||
disp.ui.mode = mode;
|
|
||||||
disp.ui.status_enabled[DISPLAY_STATUS_USB] = (mode == MODE_TYPE_USB);
|
|
||||||
disp.ui.status_enabled[DISPLAY_STATUS_BLE] = (mode == MODE_TYPE_BLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 最新原型只显示 NumLock 和 CapsLock,不再展示 ScrollLock。 */
|
|
||||||
static void display_update_keyboard_led_state(uint8_t led_mask)
|
|
||||||
{
|
|
||||||
disp.ui.led_mask = led_mask;
|
|
||||||
disp.ui.status_enabled[DISPLAY_STATUS_NUMLOCK] =
|
|
||||||
(led_mask & KEYBOARD_LED_MASK_NUM_LOCK) != 0U;
|
|
||||||
disp.ui.status_enabled[DISPLAY_STATUS_CAPSLOCK] =
|
|
||||||
(led_mask & KEYBOARD_LED_MASK_CAPS_LOCK) != 0U;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 底部状态条的亮灭与边框颜色联动更新,保持原型机视觉语言。 */
|
|
||||||
static void display_refresh_status_bar_locked(void)
|
|
||||||
{
|
|
||||||
for (uint32_t i = 0; i < DISPLAY_STATUS_COUNT; i++)
|
|
||||||
{
|
|
||||||
lv_obj_t *badge = disp.ui.status_badges[i];
|
|
||||||
lv_obj_t *label = disp.ui.status_labels[i];
|
|
||||||
bool active = disp.ui.status_enabled[i];
|
|
||||||
|
|
||||||
if (!badge || !label)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
lv_obj_set_style_border_width(badge, 4, 0);
|
|
||||||
lv_obj_set_style_border_color(badge,
|
|
||||||
active ? disp.ui.theme_color : disp.ui.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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 电池图标、百分比和状态图标分开更新,便于独立配色。 */
|
|
||||||
static void display_refresh_battery_locked(void)
|
|
||||||
{
|
|
||||||
char battery_text[8];
|
|
||||||
lv_color_t battery_color;
|
|
||||||
const char *state_symbol = "";
|
|
||||||
lv_color_t state_color = lv_color_white();
|
|
||||||
|
|
||||||
if (!disp.ui.battery_icon || !disp.ui.battery_label || !disp.ui.battery_state_label)
|
|
||||||
return;
|
|
||||||
|
|
||||||
battery_color = display_get_battery_color(disp.ui.battery_level);
|
|
||||||
snprintk(battery_text, sizeof(battery_text), "%u%%", disp.ui.battery_level);
|
|
||||||
|
|
||||||
if ((disp.ui.battery_flags & BATTERY_STATUS_FLAG_FULL) != 0U)
|
|
||||||
{
|
|
||||||
state_symbol = DISPLAY_SYMBOL_PLUG;
|
|
||||||
state_color = lv_color_hex(0x4C9EF5);
|
|
||||||
}
|
|
||||||
else if ((disp.ui.battery_flags & BATTERY_STATUS_FLAG_CHARGING) != 0U)
|
|
||||||
{
|
|
||||||
state_symbol = LV_SYMBOL_CHARGE;
|
|
||||||
state_color = lv_color_hex(0xF4D35E);
|
|
||||||
}
|
|
||||||
|
|
||||||
lv_label_set_text(disp.ui.battery_icon,
|
|
||||||
display_get_battery_symbol(disp.ui.battery_level));
|
|
||||||
lv_obj_set_style_text_color(disp.ui.battery_icon, battery_color, 0);
|
|
||||||
lv_label_set_text(disp.ui.battery_label, battery_text);
|
|
||||||
lv_label_set_text(disp.ui.battery_state_label, state_symbol);
|
|
||||||
lv_obj_set_style_text_color(disp.ui.battery_state_label, state_color, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 时间优先显示 time_manager 的真实快照。
|
|
||||||
* 如果当前尚未同步,则退回到固定基准上的 demo 时间,保证 UI 结构始终可见。
|
|
||||||
*/
|
|
||||||
static void display_refresh_datetime_locked(void)
|
|
||||||
{
|
|
||||||
struct time_manager_snapshot snapshot;
|
|
||||||
char date_text[16];
|
|
||||||
char time_text[16];
|
|
||||||
int err = time_manager_get_snapshot(&snapshot);
|
|
||||||
|
|
||||||
if (!disp.ui.date_label || !disp.ui.time_label)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!err)
|
|
||||||
{
|
|
||||||
time_t local_seconds;
|
|
||||||
struct tm tm_buf;
|
|
||||||
struct tm *tm_info;
|
|
||||||
|
|
||||||
local_seconds = (time_t)(snapshot.utc_ms / 1000ULL) +
|
|
||||||
(time_t)((int32_t)snapshot.timezone_min * 60);
|
|
||||||
tm_info = gmtime_r(&local_seconds, &tm_buf);
|
|
||||||
|
|
||||||
if (tm_info)
|
|
||||||
{
|
|
||||||
unsigned int year = (unsigned int)(tm_info->tm_year + 1900);
|
|
||||||
unsigned int month = (unsigned int)(tm_info->tm_mon + 1);
|
|
||||||
unsigned int day = (unsigned int)tm_info->tm_mday;
|
|
||||||
unsigned int hour = (unsigned int)tm_info->tm_hour;
|
|
||||||
unsigned int minute = (unsigned int)tm_info->tm_min;
|
|
||||||
unsigned int second = (unsigned int)tm_info->tm_sec;
|
|
||||||
|
|
||||||
snprintk(date_text, sizeof(date_text), "%04u/%02u/%02u",
|
|
||||||
year, month, day);
|
|
||||||
snprintk(time_text, sizeof(time_text), "%02u:%02u:%02u",
|
|
||||||
hour, minute, second);
|
|
||||||
lv_label_set_text(disp.ui.date_label, date_text);
|
|
||||||
lv_label_set_text(disp.ui.time_label, time_text);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
uint32_t seconds = disp.tick_count;
|
|
||||||
uint32_t hour = (DISPLAY_DEMO_BASE_HOUR + (seconds / 3600U)) % 24U;
|
|
||||||
uint32_t minute = (DISPLAY_DEMO_BASE_MIN + ((seconds / 60U) % 60U)) % 60U;
|
|
||||||
uint32_t second = (DISPLAY_DEMO_BASE_SEC + (seconds % 60U)) % 60U;
|
|
||||||
|
|
||||||
snprintk(date_text, sizeof(date_text), "%04d/%02d/%02d",
|
|
||||||
DISPLAY_DEMO_BASE_YEAR,
|
|
||||||
DISPLAY_DEMO_BASE_MONTH,
|
|
||||||
DISPLAY_DEMO_BASE_DAY);
|
|
||||||
snprintk(time_text, sizeof(time_text), "%02u:%02u:%02u",
|
|
||||||
hour, minute, second);
|
|
||||||
lv_label_set_text(disp.ui.date_label, date_text);
|
|
||||||
lv_label_set_text(disp.ui.time_label, time_text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 一次性把缓存状态刷到 UI,避免控件创建与状态恢复互相耦合。 */
|
|
||||||
static void display_refresh_all_locked(void)
|
|
||||||
{
|
|
||||||
display_refresh_status_bar_locked();
|
|
||||||
display_refresh_battery_locked();
|
|
||||||
display_refresh_datetime_locked();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 状态 badge 保持原型尺寸和圆角,确保在 320x172 面板上视觉一致。 */
|
|
||||||
static void display_create_status_chip_locked(lv_obj_t *parent, enum display_status_id id)
|
|
||||||
{
|
|
||||||
lv_obj_t *badge = lv_obj_create(parent);
|
|
||||||
lv_obj_t *label;
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
label = lv_label_create(badge);
|
|
||||||
lv_label_set_text(label, g_status_texts[id]);
|
|
||||||
lv_obj_set_width(label, LV_PCT(100));
|
|
||||||
lv_obj_set_style_text_font(label, &ui_font_keyboard_small_18, 0);
|
|
||||||
lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_CENTER, 0);
|
|
||||||
lv_obj_center(label);
|
|
||||||
|
|
||||||
disp.ui.status_badges[id] = badge;
|
|
||||||
disp.ui.status_labels[id] = label;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* UI 直接内联到 display_module,保留原型布局而不引入额外 ui 抽象层。 */
|
|
||||||
static void display_create_ui_locked(void)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
|
|
||||||
lv_obj_clean(screen);
|
|
||||||
lv_obj_set_style_bg_color(screen, lv_color_hex(0x0F1115), 0);
|
|
||||||
lv_obj_set_style_bg_grad_color(screen, lv_color_hex(0x1A1F29), 0);
|
|
||||||
lv_obj_set_style_bg_grad_dir(screen, LV_GRAD_DIR_VER, 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);
|
|
||||||
lv_obj_remove_style_all(content);
|
|
||||||
lv_obj_set_size(content, LV_PCT(100), LV_PCT(100));
|
|
||||||
lv_obj_set_style_bg_color(content, lv_color_hex(0x0F1115), 0);
|
|
||||||
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_color(top_row, lv_color_hex(0x0F1115), 0);
|
|
||||||
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);
|
|
||||||
|
|
||||||
disp.ui.date_label = lv_label_create(top_row);
|
|
||||||
lv_obj_set_style_text_font(disp.ui.date_label, &ui_font_keyboard_small_18, 0);
|
|
||||||
lv_obj_set_style_text_color(disp.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);
|
|
||||||
|
|
||||||
disp.ui.battery_icon = lv_label_create(battery_wrap);
|
|
||||||
lv_obj_set_style_text_font(disp.ui.battery_icon, &ui_font_keyboard_small_18, 0);
|
|
||||||
|
|
||||||
disp.ui.battery_label = lv_label_create(battery_wrap);
|
|
||||||
lv_obj_set_style_text_font(disp.ui.battery_label, &ui_font_keyboard_small_18, 0);
|
|
||||||
lv_obj_set_style_text_color(disp.ui.battery_label, lv_color_hex(0xD8DEE9), 0);
|
|
||||||
|
|
||||||
disp.ui.battery_state_label = lv_label_create(battery_wrap);
|
|
||||||
lv_obj_set_style_text_font(disp.ui.battery_state_label, &ui_font_keyboard_small_18, 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_color(middle_row, lv_color_hex(0x0F1115), 0);
|
|
||||||
lv_obj_set_style_bg_opa(middle_row, LV_OPA_TRANSP, 0);
|
|
||||||
|
|
||||||
disp.ui.time_label = lv_label_create(middle_row);
|
|
||||||
lv_obj_set_style_text_font(disp.ui.time_label, &ui_font_keyboard_time_48, 0);
|
|
||||||
lv_obj_set_style_text_color(disp.ui.time_label, lv_color_white(), 0);
|
|
||||||
lv_obj_center(disp.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_color(bottom_row, lv_color_hex(0x0F1115), 0);
|
|
||||||
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 < DISPLAY_STATUS_COUNT; i++)
|
|
||||||
display_create_status_chip_locked(bottom_row, (enum display_status_id)i);
|
|
||||||
|
|
||||||
display_refresh_all_locked();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 周期刷新只负责时间区域;状态图标改为事件驱动,避免无谓重绘。 */
|
|
||||||
static void display_update_work_fn(struct k_work *work)
|
static void display_update_work_fn(struct k_work *work)
|
||||||
{
|
{
|
||||||
ARG_UNUSED(work);
|
ARG_UNUSED(work);
|
||||||
@@ -637,15 +346,15 @@ static void display_update_work_fn(struct k_work *work)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
disp.tick_count++;
|
disp.tick_count++;
|
||||||
|
display_update_datetime_text();
|
||||||
|
|
||||||
lvgl_lock();
|
lvgl_lock();
|
||||||
display_refresh_datetime_locked();
|
display_ui_refresh_datetime(disp.date_text, disp.time_text);
|
||||||
lvgl_unlock();
|
lvgl_unlock();
|
||||||
|
|
||||||
display_schedule_update(K_MSEC(DISPLAY_UPDATE_PERIOD_MS));
|
display_schedule_update(K_MSEC(DISPLAY_UPDATE_PERIOD_MS));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 显示初始化完成后,后续 UI 更新全部通过事件和定时刷新驱动。 */
|
|
||||||
static int display_init(void)
|
static int display_init(void)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
@@ -658,15 +367,16 @@ static int display_init(void)
|
|||||||
|
|
||||||
display_get_capabilities(disp.dev, &disp.caps);
|
display_get_capabilities(disp.dev, &disp.caps);
|
||||||
LOG_INF("Display caps: %ux%u fmt=%d",
|
LOG_INF("Display caps: %ux%u fmt=%d",
|
||||||
disp.caps.x_resolution,
|
disp.caps.x_resolution,
|
||||||
disp.caps.y_resolution,
|
disp.caps.y_resolution,
|
||||||
disp.caps.current_pixel_format);
|
disp.caps.current_pixel_format);
|
||||||
|
|
||||||
k_work_init_delayable(&disp.update_work, display_update_work_fn);
|
k_work_init_delayable(&disp.update_work, display_update_work_fn);
|
||||||
k_work_init_delayable(&disp.idle_work, display_idle_timeout_fn);
|
k_work_init_delayable(&disp.idle_work, display_idle_timeout_fn);
|
||||||
k_work_init_delayable(&disp.theme_save_work, display_theme_save_work_fn);
|
k_work_init_delayable(&disp.theme_save_work, display_theme_save_work_fn);
|
||||||
disp.tick_count = 0U;
|
disp.tick_count = 0U;
|
||||||
display_theme_apply_loaded_storage();
|
display_theme_apply_loaded_storage();
|
||||||
|
display_update_datetime_text();
|
||||||
|
|
||||||
err = display_blanking_off(disp.dev);
|
err = display_blanking_off(disp.dev);
|
||||||
if (err)
|
if (err)
|
||||||
@@ -680,7 +390,7 @@ static int display_init(void)
|
|||||||
return err;
|
return err;
|
||||||
|
|
||||||
lvgl_lock();
|
lvgl_lock();
|
||||||
display_create_ui_locked();
|
display_ui_init(&disp.ui, disp.date_text, disp.time_text);
|
||||||
lvgl_unlock();
|
lvgl_unlock();
|
||||||
|
|
||||||
disp.initialized = true;
|
disp.initialized = true;
|
||||||
@@ -692,54 +402,45 @@ static int display_init(void)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 电池事件只缓存最新 SOC,UI 若已就绪则立即刷新顶部电池区域。 */
|
|
||||||
static bool handle_battery_status_event(const struct battery_status_event *event)
|
static bool handle_battery_status_event(const struct battery_status_event *event)
|
||||||
{
|
{
|
||||||
disp.ui.battery_level = battery_status_event_get_soc(event);
|
disp.ui.battery_level = battery_status_event_get_soc(event);
|
||||||
disp.ui.battery_flags = battery_status_event_get_flags(event);
|
disp.ui.battery_flags = battery_status_event_get_flags(event);
|
||||||
|
|
||||||
if (!disp.initialized)
|
if (!disp.initialized || !display_is_active()) {
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!display_is_active())
|
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
lvgl_lock();
|
lvgl_lock();
|
||||||
display_refresh_battery_locked();
|
display_ui_refresh_battery(&disp.ui);
|
||||||
lvgl_unlock();
|
lvgl_unlock();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 模式事件只影响 USB/BLE 两个 badge 的亮灭。 */
|
|
||||||
static bool handle_mode_event(const struct mode_event *event)
|
static bool handle_mode_event(const struct mode_event *event)
|
||||||
{
|
{
|
||||||
display_update_mode_state(event->mode_type);
|
disp.ui.mode = event->mode_type;
|
||||||
|
|
||||||
if (!disp.initialized)
|
if (!disp.initialized || !display_is_active()) {
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!display_is_active())
|
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
lvgl_lock();
|
lvgl_lock();
|
||||||
display_refresh_status_bar_locked();
|
display_ui_refresh_status_bar(&disp.ui);
|
||||||
lvgl_unlock();
|
lvgl_unlock();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* NumLock/CapsLock/ScrollLock 变化后,底部三个状态 badge 立即更新。 */
|
|
||||||
static bool handle_keyboard_led_event(const struct keyboard_led_event *event)
|
static bool handle_keyboard_led_event(const struct keyboard_led_event *event)
|
||||||
{
|
{
|
||||||
display_update_keyboard_led_state(keyboard_led_event_get_mask(event));
|
disp.ui.led_mask = keyboard_led_event_get_mask(event);
|
||||||
|
|
||||||
if (!disp.initialized)
|
if (!disp.initialized || !display_is_active()) {
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!display_is_active())
|
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
lvgl_lock();
|
lvgl_lock();
|
||||||
display_refresh_status_bar_locked();
|
display_ui_refresh_status_bar(&disp.ui);
|
||||||
lvgl_unlock();
|
lvgl_unlock();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -753,12 +454,11 @@ static bool handle_display_theme_event(const struct display_theme_event *event)
|
|||||||
}
|
}
|
||||||
|
|
||||||
lvgl_lock();
|
lvgl_lock();
|
||||||
display_refresh_status_bar_locked();
|
display_ui_refresh_status_bar(&disp.ui);
|
||||||
lvgl_unlock();
|
lvgl_unlock();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 任意按钮事件都可点亮屏幕并重置 1 分钟空闲计时。 */
|
|
||||||
static bool handle_button_event(const struct button_event *event)
|
static bool handle_button_event(const struct button_event *event)
|
||||||
{
|
{
|
||||||
ARG_UNUSED(event);
|
ARG_UNUSED(event);
|
||||||
@@ -786,7 +486,7 @@ static bool handle_module_state_event(const struct module_state_event *event)
|
|||||||
|
|
||||||
if (disp.initialized && display_is_active()) {
|
if (disp.initialized && display_is_active()) {
|
||||||
lvgl_lock();
|
lvgl_lock();
|
||||||
display_refresh_status_bar_locked();
|
display_ui_refresh_status_bar(&disp.ui);
|
||||||
lvgl_unlock();
|
lvgl_unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
306
src/ui/display_ui.c
Normal file
306
src/ui/display_ui.c
Normal file
@@ -0,0 +1,306 @@
|
|||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <lvgl.h>
|
||||||
|
#include <zephyr/sys/printk.h>
|
||||||
|
|
||||||
|
#include "battery_status_event.h"
|
||||||
|
#include "display_ui.h"
|
||||||
|
#include "keyboard_led_event.h"
|
||||||
|
|
||||||
|
#define DISPLAY_SYMBOL_PLUG "\xEF\x87\xA6"
|
||||||
|
|
||||||
|
LV_FONT_DECLARE(ui_font_keyboard_small_18);
|
||||||
|
LV_FONT_DECLARE(ui_font_keyboard_time_48);
|
||||||
|
|
||||||
|
enum display_status_id
|
||||||
|
{
|
||||||
|
DISPLAY_STATUS_USB = 0,
|
||||||
|
DISPLAY_STATUS_BLE,
|
||||||
|
DISPLAY_STATUS_NUMLOCK,
|
||||||
|
DISPLAY_STATUS_CAPSLOCK,
|
||||||
|
DISPLAY_STATUS_COUNT,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct display_ui_ctx
|
||||||
|
{
|
||||||
|
lv_obj_t *status_badges[DISPLAY_STATUS_COUNT];
|
||||||
|
lv_obj_t *status_labels[DISPLAY_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 display_ui_ctx g_display_ui;
|
||||||
|
|
||||||
|
static const char *const g_status_texts[DISPLAY_STATUS_COUNT] = {
|
||||||
|
LV_SYMBOL_USB,
|
||||||
|
LV_SYMBOL_BLUETOOTH,
|
||||||
|
"1",
|
||||||
|
"A",
|
||||||
|
};
|
||||||
|
|
||||||
|
static lv_color_t display_ui_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 *display_ui_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 display_ui_status_is_active(enum display_status_id id,
|
||||||
|
const struct display_ui_model *model)
|
||||||
|
{
|
||||||
|
switch (id) {
|
||||||
|
case DISPLAY_STATUS_USB:
|
||||||
|
return model->mode == MODE_TYPE_USB;
|
||||||
|
|
||||||
|
case DISPLAY_STATUS_BLE:
|
||||||
|
return model->mode == MODE_TYPE_BLE;
|
||||||
|
|
||||||
|
case DISPLAY_STATUS_NUMLOCK:
|
||||||
|
return (model->led_mask & KEYBOARD_LED_MASK_NUM_LOCK) != 0U;
|
||||||
|
|
||||||
|
case DISPLAY_STATUS_CAPSLOCK:
|
||||||
|
return (model->led_mask & KEYBOARD_LED_MASK_CAPS_LOCK) != 0U;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void display_ui_create_status_chip(lv_obj_t *parent, enum display_status_id id)
|
||||||
|
{
|
||||||
|
lv_obj_t *badge = lv_obj_create(parent);
|
||||||
|
lv_obj_t *label;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
label = lv_label_create(badge);
|
||||||
|
lv_label_set_text(label, g_status_texts[id]);
|
||||||
|
lv_obj_set_width(label, LV_PCT(100));
|
||||||
|
lv_obj_set_style_text_font(label, &ui_font_keyboard_small_18, 0);
|
||||||
|
lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_CENTER, 0);
|
||||||
|
lv_obj_center(label);
|
||||||
|
|
||||||
|
g_display_ui.status_badges[id] = badge;
|
||||||
|
g_display_ui.status_labels[id] = label;
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_ui_refresh_status_bar(const struct display_ui_model *model)
|
||||||
|
{
|
||||||
|
for (uint32_t i = 0; i < DISPLAY_STATUS_COUNT; i++) {
|
||||||
|
lv_obj_t *badge = g_display_ui.status_badges[i];
|
||||||
|
lv_obj_t *label = g_display_ui.status_labels[i];
|
||||||
|
bool active = display_ui_status_is_active((enum display_status_id)i, model);
|
||||||
|
|
||||||
|
if (!badge || !label) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
lv_obj_set_style_border_width(badge, 4, 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 display_ui_refresh_battery(const struct display_ui_model *model)
|
||||||
|
{
|
||||||
|
char battery_text[8];
|
||||||
|
lv_color_t battery_color;
|
||||||
|
const char *state_symbol = "";
|
||||||
|
lv_color_t state_color = lv_color_white();
|
||||||
|
|
||||||
|
if (!g_display_ui.battery_icon ||
|
||||||
|
!g_display_ui.battery_label ||
|
||||||
|
!g_display_ui.battery_state_label) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
battery_color = display_ui_get_battery_color(model->battery_level);
|
||||||
|
snprintk(battery_text, sizeof(battery_text), "%u%%", model->battery_level);
|
||||||
|
|
||||||
|
if ((model->battery_flags & BATTERY_STATUS_FLAG_FULL) != 0U) {
|
||||||
|
state_symbol = DISPLAY_SYMBOL_PLUG;
|
||||||
|
state_color = lv_color_hex(0x4C9EF5);
|
||||||
|
} else if ((model->battery_flags & BATTERY_STATUS_FLAG_CHARGING) != 0U) {
|
||||||
|
state_symbol = LV_SYMBOL_CHARGE;
|
||||||
|
state_color = lv_color_hex(0xF4D35E);
|
||||||
|
}
|
||||||
|
|
||||||
|
lv_label_set_text(g_display_ui.battery_icon,
|
||||||
|
display_ui_get_battery_symbol(model->battery_level));
|
||||||
|
lv_obj_set_style_text_color(g_display_ui.battery_icon, battery_color, 0);
|
||||||
|
lv_label_set_text(g_display_ui.battery_label, battery_text);
|
||||||
|
lv_label_set_text(g_display_ui.battery_state_label, state_symbol);
|
||||||
|
lv_obj_set_style_text_color(g_display_ui.battery_state_label, state_color, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_ui_refresh_datetime(const char *date_text, const char *time_text)
|
||||||
|
{
|
||||||
|
if (!g_display_ui.date_label || !g_display_ui.time_label) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lv_label_set_text(g_display_ui.date_label, date_text);
|
||||||
|
lv_label_set_text(g_display_ui.time_label, time_text);
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_ui_refresh_all(const struct display_ui_model *model,
|
||||||
|
const char *date_text,
|
||||||
|
const char *time_text)
|
||||||
|
{
|
||||||
|
display_ui_refresh_status_bar(model);
|
||||||
|
display_ui_refresh_battery(model);
|
||||||
|
display_ui_refresh_datetime(date_text, time_text);
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_ui_init(const struct display_ui_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;
|
||||||
|
|
||||||
|
memset(&g_display_ui, 0, sizeof(g_display_ui));
|
||||||
|
|
||||||
|
lv_obj_clean(screen);
|
||||||
|
lv_obj_set_style_bg_color(screen, lv_color_hex(0x0F1115), 0);
|
||||||
|
lv_obj_set_style_bg_grad_color(screen, lv_color_hex(0x1A1F29), 0);
|
||||||
|
lv_obj_set_style_bg_grad_dir(screen, LV_GRAD_DIR_VER, 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);
|
||||||
|
lv_obj_remove_style_all(content);
|
||||||
|
lv_obj_set_size(content, LV_PCT(100), LV_PCT(100));
|
||||||
|
lv_obj_set_style_bg_color(content, lv_color_hex(0x0F1115), 0);
|
||||||
|
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_color(top_row, lv_color_hex(0x0F1115), 0);
|
||||||
|
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_display_ui.date_label = lv_label_create(top_row);
|
||||||
|
lv_obj_set_style_text_font(g_display_ui.date_label, &ui_font_keyboard_small_18, 0);
|
||||||
|
lv_obj_set_style_text_color(g_display_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_display_ui.battery_icon = lv_label_create(battery_wrap);
|
||||||
|
lv_obj_set_style_text_font(g_display_ui.battery_icon, &ui_font_keyboard_small_18, 0);
|
||||||
|
|
||||||
|
g_display_ui.battery_label = lv_label_create(battery_wrap);
|
||||||
|
lv_obj_set_style_text_font(g_display_ui.battery_label, &ui_font_keyboard_small_18, 0);
|
||||||
|
lv_obj_set_style_text_color(g_display_ui.battery_label, lv_color_hex(0xD8DEE9), 0);
|
||||||
|
|
||||||
|
g_display_ui.battery_state_label = lv_label_create(battery_wrap);
|
||||||
|
lv_obj_set_style_text_font(g_display_ui.battery_state_label, &ui_font_keyboard_small_18, 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_color(middle_row, lv_color_hex(0x0F1115), 0);
|
||||||
|
lv_obj_set_style_bg_opa(middle_row, LV_OPA_TRANSP, 0);
|
||||||
|
|
||||||
|
g_display_ui.time_label = lv_label_create(middle_row);
|
||||||
|
lv_obj_set_style_text_font(g_display_ui.time_label, &ui_font_keyboard_time_48, 0);
|
||||||
|
lv_obj_set_style_text_color(g_display_ui.time_label, lv_color_white(), 0);
|
||||||
|
lv_obj_center(g_display_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_color(bottom_row, lv_color_hex(0x0F1115), 0);
|
||||||
|
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 < DISPLAY_STATUS_COUNT; i++) {
|
||||||
|
display_ui_create_status_chip(bottom_row, (enum display_status_id)i);
|
||||||
|
}
|
||||||
|
|
||||||
|
display_ui_refresh_all(model, date_text, time_text);
|
||||||
|
}
|
||||||
34
src/ui/display_ui.h
Normal file
34
src/ui/display_ui.h
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
#ifndef DISPLAY_UI_H
|
||||||
|
#define DISPLAY_UI_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <lvgl.h>
|
||||||
|
|
||||||
|
#include "mode_event.h"
|
||||||
|
|
||||||
|
struct display_ui_model
|
||||||
|
{
|
||||||
|
lv_color_t theme_color;
|
||||||
|
lv_color_t inactive_border_color;
|
||||||
|
uint8_t battery_level;
|
||||||
|
mode_type_t mode;
|
||||||
|
uint8_t led_mask;
|
||||||
|
uint8_t battery_flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
void display_ui_init(const struct display_ui_model *model,
|
||||||
|
const char *date_text,
|
||||||
|
const char *time_text);
|
||||||
|
|
||||||
|
void display_ui_refresh_all(const struct display_ui_model *model,
|
||||||
|
const char *date_text,
|
||||||
|
const char *time_text);
|
||||||
|
|
||||||
|
void display_ui_refresh_status_bar(const struct display_ui_model *model);
|
||||||
|
|
||||||
|
void display_ui_refresh_battery(const struct display_ui_model *model);
|
||||||
|
|
||||||
|
void display_ui_refresh_datetime(const char *date_text, const char *time_text);
|
||||||
|
|
||||||
|
#endif /* DISPLAY_UI_H */
|
||||||
Reference in New Issue
Block a user