主题系统¶
概述¶
EmbeddedGUI 提供了一套基于样式描述符的主题系统,支持亮色和暗色两套内置主题,并允许运行时切换。主题通过 egui_theme_t 结构体为每种控件定义不同状态下的视觉样式,所有样式数据存储在 ROM 中,零 RAM 开销。
架构¶
src/config/egui_config_default.h -- 主题色板宏 (EGUI_THEME_PRIMARY 等)
src/style/egui_style.h -- 样式结构体和查询 API
src/style/egui_theme.h -- 主题结构体定义
src/style/egui_theme_light.c -- 亮色主题样式数据
src/style/egui_theme_dark.c -- 暗色主题样式数据
src/style/egui_theme.c -- 主题切换逻辑
核心数据结构¶
// 单个样式 (~32 bytes, const ROM)
typedef struct egui_style
{
uint16_t flags; // 哪些属性有效
egui_color_t bg_color; // 背景色
egui_alpha_t bg_alpha; // 背景透明度
const egui_gradient_t *bg_gradient; // 渐变背景
egui_color_t border_color; // 边框色
egui_dim_t border_width; // 边框宽度
egui_dim_t radius; // 圆角半径
egui_dim_t pad_top, pad_bottom, pad_left, pad_right; // 内边距
egui_color_t text_color; // 文字颜色
const egui_font_t *text_font; // 字体
const egui_shadow_t *shadow; // 阴影
} egui_style_t;
// 控件样式描述符: [part][state] 二维查找表
typedef struct egui_widget_style_desc
{
uint8_t part_count;
const egui_style_t *const *styles; // styles[part * STATE_MAX + state]
} egui_widget_style_desc_t;
// 主题: 为每种控件提供样式描述符
typedef struct egui_theme
{
const char *name;
const egui_widget_style_desc_t *button;
const egui_widget_style_desc_t *label;
const egui_widget_style_desc_t *switch_ctrl;
const egui_widget_style_desc_t *slider;
const egui_widget_style_desc_t *checkbox;
const egui_widget_style_desc_t *card;
const egui_widget_style_desc_t *progress_bar;
const egui_widget_style_desc_t *circular_progress_bar;
} egui_theme_t;
Part 和 State¶
样式通过 Part(控件部件)和 State(交互状态)两个维度索引:
Part |
说明 |
示例 |
|---|---|---|
|
主体 |
按钮背景、标签背景 |
|
指示器 |
进度条填充、滑块轨道 |
|
旋钮 |
滑块手柄、开关圆点 |
|
选中态 |
复选框选中背景 |
State |
说明 |
|---|---|
|
默认状态 |
|
按下状态 |
|
禁用状态 |
|
焦点状态 |
|
选中状态 |
内置主题¶
色板¶
主题色板通过宏定义在 src/config/egui_config_default.h 中,可在 app_egui_config.h 中覆盖:
宏 |
默认值 |
说明 |
|---|---|---|
|
#2563EB (Blue 600) |
主色调 |
|
#1D4ED8 (Blue 700) |
主色调深色(按下态) |
|
#3B82F6 (Blue 500) |
主色调浅色 |
|
#14B8A6 (Teal 500) |
辅助色 |
|
#16A34A (Green 600) |
成功色 |
|
#F59E0B (Amber 500) |
警告色 |
|
#DC2626 (Red 600) |
危险色 |
|
#FFFFFF |
表面色 |
|
#111827 (Gray 900) |
主文字色 |
|
#6B7280 (Gray 500) |
次要文字色 |
亮色主题 (egui_theme_light)¶
定义在 src/style/egui_theme_light.c,使用浅色背景、深色文字,带阴影效果。
暗色主题 (egui_theme_dark)¶
定义在 src/style/egui_theme_dark.c,使用深色背景、浅色文字,适合低光环境。
运行时切换¶
// 切换到暗色主题
egui_theme_set(core, &egui_theme_dark);
// 切换到亮色主题
egui_theme_set(core, &egui_theme_light);
// 获取当前主题
const egui_theme_t *theme = egui_theme_get(core);
egui_theme_set(core, theme) 内部调用 egui_core_force_refresh(core) 强制全屏重绘,确保当前 core 上的所有控件立即应用新主题。
控件如何响应主题¶
控件在 on_draw 中查询当前主题获取样式,以 button 为例:
void egui_view_button_on_draw(egui_view_t *self)
{
// 从当前 core 的主题获取 button 的样式描述符
const egui_theme_t *theme = egui_theme_get(egui_view_get_core(self));
const egui_widget_style_desc_t *desc =
theme ? theme->button : NULL;
// 根据当前状态(normal/pressed/disabled)获取对应样式
const egui_style_t *style =
desc ? egui_style_get_current(desc, EGUI_PART_MAIN, self) : NULL;
if (style)
{
// 使用 style->bg_color, style->radius, style->text_color 等绘制
}
}
egui_style_get_current() 是一个 O(1) 的内联查找,自动根据视图的 is_pressed、is_enable 等状态位映射到对应的 egui_state_t。
自定义主题¶
方法一:覆盖色板宏¶
在 app_egui_config.h 中重定义色板宏,所有内置主题自动使用新颜色:
// app_egui_config.h
#define EGUI_THEME_PRIMARY EGUI_COLOR_MAKE(0x10, 0xB9, 0x81) // Emerald
#define EGUI_THEME_PRIMARY_DARK EGUI_COLOR_MAKE(0x05, 0x9D, 0x69)
方法二:创建全新主题¶
定义自己的样式数据和主题结构体:
// 定义按钮正常态样式
static const egui_style_t my_btn_normal = {
.flags = EGUI_STYLE_PROP_BG_COLOR | EGUI_STYLE_PROP_RADIUS | EGUI_STYLE_PROP_TEXT_COLOR,
.bg_color = EGUI_COLOR_MAKE(0xFF, 0x69, 0x00),
.bg_alpha = EGUI_ALPHA_COVER,
.radius = 8,
.text_color = EGUI_COLOR_WHITE,
.text_alpha = EGUI_ALPHA_COVER,
};
// ... 定义其他状态和控件的样式 ...
// 组装主题
const egui_theme_t my_custom_theme = {
.name = "custom",
.button = &my_btn_desc,
.label = &my_label_desc,
// ... 其他控件 ...
};
// 使用
egui_theme_set(core, &my_custom_theme);
主题结构体中未赋值的控件指针为 NULL,对应控件会回退到硬编码的默认外观。