主题系统

概述

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

说明

示例

EGUI_PART_MAIN

主体

按钮背景、标签背景

EGUI_PART_INDICATOR

指示器

进度条填充、滑块轨道

EGUI_PART_KNOB

旋钮

滑块手柄、开关圆点

EGUI_PART_SELECTED

选中态

复选框选中背景

State

说明

EGUI_STATE_NORMAL

默认状态

EGUI_STATE_PRESSED

按下状态

EGUI_STATE_DISABLED

禁用状态

EGUI_STATE_FOCUSED

焦点状态

EGUI_STATE_CHECKED

选中状态

内置主题

色板

主题色板通过宏定义在 src/config/egui_config_default.h 中,可在 app_egui_config.h 中覆盖:

默认值

说明

EGUI_THEME_PRIMARY

#2563EB (Blue 600)

主色调

EGUI_THEME_PRIMARY_DARK

#1D4ED8 (Blue 700)

主色调深色(按下态)

EGUI_THEME_PRIMARY_LIGHT

#3B82F6 (Blue 500)

主色调浅色

EGUI_THEME_SECONDARY

#14B8A6 (Teal 500)

辅助色

EGUI_THEME_SUCCESS

#16A34A (Green 600)

成功色

EGUI_THEME_WARNING

#F59E0B (Amber 500)

警告色

EGUI_THEME_DANGER

#DC2626 (Red 600)

危险色

EGUI_THEME_SURFACE

#FFFFFF

表面色

EGUI_THEME_TEXT_PRIMARY

#111827 (Gray 900)

主文字色

EGUI_THEME_TEXT_SECONDARY

#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_pressedis_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,对应控件会回退到硬编码的默认外观。