第一个应用

本文以 HelloSimple 为基础,演示如何用 EmbeddedGUI 构建一个最小可运行界面。这个示例适合作为入门起点,也能作为 code size 的基础基线。

它的特点是:

  • 只使用两个 Label

  • 底部标签可点击,点击后会切换成固定提示 CLICKED

  • 不使用 LinearLayout

  • 不使用 egui_api_sprintf

  • 不使用 egui_view_button_t

  • 不依赖默认字体资源

  • 在 app 配置里启用更轻量的 label compact 路径,并关闭一些不需要的功能开关

运行 HelloSimple

先确保 环境搭建 已完成,然后在项目根目录执行:

make all APP=HelloSimple
make run

运行后,你会看到一个居中的 HELLO EGUI 标签和一个 CLICK 标签。点击底部标签后,它的文本会变为 CLICKED

完整源代码

以下是 example/HelloSimple/uicode_disp0.c 的完整代码:

#include "egui.h"
#include "uicode_disp0.h"

static egui_view_label_t label_1;
static egui_view_label_t action_1;

#define ACTION_WIDTH  150
#define ACTION_HEIGHT 50

#define LABEL_WIDTH  150
#define LABEL_HEIGHT 50

#define VIEW_GAP  12
#define LABEL_X   ((EGUI_CONFIG_SCREEN_WIDTH - LABEL_WIDTH) / 2)
#define LABEL_Y   ((EGUI_CONFIG_SCREEN_HEIGHT - LABEL_HEIGHT - ACTION_HEIGHT - VIEW_GAP) / 2)
#define ACTION_X  ((EGUI_CONFIG_SCREEN_WIDTH - ACTION_WIDTH) / 2)
#define ACTION_Y  (LABEL_Y + LABEL_HEIGHT + VIEW_GAP)

EGUI_VIEW_LABEL_PARAMS_INIT(label_1_params, LABEL_X, LABEL_Y, LABEL_WIDTH, LABEL_HEIGHT, "HELLO EGUI", NULL, EGUI_COLOR_WHITE, EGUI_ALPHA_100);
EGUI_VIEW_LABEL_PARAMS_INIT(action_1_params, ACTION_X, ACTION_Y, ACTION_WIDTH, ACTION_HEIGHT, "CLICK", NULL, EGUI_COLOR_WHITE, EGUI_ALPHA_100);

static void action_click_cb(egui_view_t *self)
{
    egui_view_label_set_text(self, "CLICKED");
}

void uicode_disp0_init(egui_core_t *core)
{
    egui_view_label_init_with_params(EGUI_VIEW_OF(&label_1), core, &label_1_params);
    egui_view_label_init_with_params(EGUI_VIEW_OF(&action_1), core, &action_1_params);
    egui_view_set_on_click_listener(EGUI_VIEW_OF(&action_1), action_click_cb);

    egui_core_add_user_root_view(EGUI_VIEW_OF(&label_1));
    egui_core_add_user_root_view(EGUI_VIEW_OF(&action_1));
}

#if EGUI_CONFIG_FUNCTION_RECORDING_TEST
bool egui_port_get_recording_action(int action_index, egui_sim_action_t *p_action)
{
    if (action_index >= 3)
    {
        return false;
    }

    EGUI_SIM_SET_CLICK_VIEW(p_action, &action_1, 1000);
    return true;
}
#endif

逐段解析

1. 静态控件声明

static egui_view_label_t label_1;
static egui_view_label_t action_1;

EGUI 使用面向对象风格的 C 结构体来表示控件。这里声明了两个静态对象:

  • label_1:显示 HELLO EGUI

  • action_1:接收点击事件并更新自己的文本

把控件声明成 static 全局变量,意味着整个应用生命周期内都能稳定访问它们,不需要动态分配内存。

2. 编译期布局参数

#define VIEW_GAP  12
#define LABEL_X   ((EGUI_CONFIG_SCREEN_WIDTH - LABEL_WIDTH) / 2)
#define LABEL_Y   ((EGUI_CONFIG_SCREEN_HEIGHT - LABEL_HEIGHT - ACTION_HEIGHT - VIEW_GAP) / 2)
#define ACTION_X  ((EGUI_CONFIG_SCREEN_WIDTH - ACTION_WIDTH) / 2)
#define ACTION_Y  (LABEL_Y + LABEL_HEIGHT + VIEW_GAP)

这个示例直接使用固定坐标来排版,而不是额外创建一个 LinearLayout。这样做有两个好处:

  • 代码更短,适合作为入门示例

  • 不会额外把布局容器相关逻辑引入到最小基线里

位置仍然通过屏幕宽高宏计算,所以在同一分辨率配置下依然保持居中显示。

3. 参数宏初始化

EGUI_VIEW_LABEL_PARAMS_INIT(label_1_params, LABEL_X, LABEL_Y, LABEL_WIDTH, LABEL_HEIGHT, "HELLO EGUI", NULL, EGUI_COLOR_WHITE, EGUI_ALPHA_100);
EGUI_VIEW_LABEL_PARAMS_INIT(action_1_params, ACTION_X, ACTION_Y, ACTION_WIDTH, ACTION_HEIGHT, "CLICK", NULL, EGUI_COLOR_WHITE, EGUI_ALPHA_100);

这些 PARAMS_INIT 宏会在编译期生成参数结构体,常见字段包括:

  • 位置:xy

  • 尺寸:宽度、高度

  • 文本:显示字符串

  • 字体与颜色:这里把字体设为 NULL,让 Label 走轻量路径;颜色为白色,100% 不透明

把参数放在编译期常量里,通常比运行时逐项赋值更省 RAM。

4. 点击回调

static void action_click_cb(egui_view_t *self)
{
    egui_view_label_set_text(self, "CLICKED");
}

这里直接把第二个标签设为 clickable,因此可以复用 egui_view_label_set_text() 修改它自己的文字,而不需要额外引入 Button 控件。

这样可以避免把不必要的格式化逻辑带进 HelloSimple,也更符合“最小可运行示例”的定位。

5. 构建视图树

void uicode_disp0_init(egui_core_t *core)
{
    egui_view_label_init_with_params(EGUI_VIEW_OF(&label_1), core, &label_1_params);
    egui_view_label_init_with_params(EGUI_VIEW_OF(&action_1), core, &action_1_params);
    egui_view_set_on_click_listener(EGUI_VIEW_OF(&action_1), action_click_cb);

    egui_core_add_user_root_view(EGUI_VIEW_OF(&label_1));
    egui_core_add_user_root_view(EGUI_VIEW_OF(&action_1));
}

初始化流程非常直接:

  1. 调用 *_init_with_params() 初始化控件

  2. 给底部标签注册点击回调

  3. 把两个控件直接挂到 user root

EGUI_VIEW_OF() 用来把具体控件指针向上转换成基类 egui_view_t *,这是 EGUI 面向对象 C 风格 API 的基础用法。

6. 录制动作

#if EGUI_CONFIG_FUNCTION_RECORDING_TEST
bool egui_port_get_recording_action(int action_index, egui_sim_action_t *p_action)
{
    if (action_index >= 3)
    {
        return false;
    }

    EGUI_SIM_SET_CLICK_VIEW(p_action, &action_1, 1000);
    return true;
}
#endif

这段代码只在录制/运行时验证模式下启用,用来自动点击底部标签三次,方便 CI 和截图验证。

关键概念总结

面向对象的 C 风格

EGUI 的点击回调定义在基类 egui_view_t 上,因此像 Label 这样的基础控件也能直接注册点击事件,不一定要额外引入 Button

为什么这个示例不用 LinearLayout

HelloSimple 的目标是“最小可运行示例”,不是“布局能力演示”。当界面只有两个固定控件时,直接坐标布局更适合:

  • 更容易读懂

  • 更容易做 code size 基线

  • 不会额外引入布局容器依赖

如果你想学习容器布局,建议看 HelloBasic/linearlayout

为什么这里不用 sprintf

把按钮文案改为固定字符串,能避免:

  • 格式化函数

  • 中间缓冲区

  • 点击计数状态

这对资源受限平台更友好,也更符合 HelloSimple 作为最小示例的定位。

为什么这里关闭一些可选功能

HelloSimple 只有一个可点击标签,不需要复杂的文本格式化、触摸捕获或滚动预处理能力,因此可以关闭一些不必要的功能开关,减少最终镜像体积。

PFB 工作方式

EGUI 使用 Partial Frame Buffer,而不是整屏帧缓冲。渲染时会把屏幕拆成多个小块,逐块绘制、逐块刷新,因此只需要几 KB RAM 就能驱动一整屏 UI。

练习建议

  1. 修改标签文本,把 HELLO EGUI 换成你自己的内容。

  2. 修改 LABEL_X / LABEL_Y / ACTION_X / ACTION_Y,观察控件位置变化。

  3. 再加一个按钮,并继续用 egui_core_add_user_root_view() 直接挂到 root。

  4. 如果你想体验自动布局,再去看 HelloBasic/linearlayout,对比两种写法的差别。

下一步