Dialog 对话框

当前实现更新

以下说明以当前代码实现为准:

  • Dialog 必须在 egui_dialog_init(self, core) 时直接绑定目标 core

  • dialog.root_viewdialog.user_root_view 会在 init 阶段一起带上同一个 core

  • egui_dialog_start(self, activity) 要求 dialogdialog.root_viewdialog.user_root_view 与目标 activity 已经属于同一个 core

  • 当前实现不会在启动时自动改写 dialog / root_view / user_root_viewcore 归属

bind_core 相关旧接口已经删除,多屏场景下应在构造 Dialog 时就传入目标屏幕的 core

Dialog 是 EmbeddedGUI 中的模态对话框组件,依附于 Activity 存在。Dialog 拥有与 Activity 类似的完整生命周期,支持自定义内容和过渡动画。

Dialog 与 Activity 的关系

  • Dialog 必须绑定到一个 Activity(通过 bind_activity 字段)

  • Dialog 显示时,宿主 Activity 仍然可见,但 Dialog 覆盖在其上方

  • Dialog 拥有独立的 root_view(半透明遮罩层)和 user_root_view(内容区域)

  • 关闭 Dialog 后,焦点回到宿主 Activity

Dialog 生命周期

Dialog 的生命周期与 Activity 完全一致:

onCreate -> onStart -> onResume -> onPause -> onStop -> onDestroy
struct egui_dialog_api
{
    void (*on_create)(egui_dialog_t *self);
    void (*on_start)(egui_dialog_t *self);
    void (*on_resume)(egui_dialog_t *self);
    void (*on_pause)(egui_dialog_t *self);
    void (*on_stop)(egui_dialog_t *self);
    void (*on_destroy)(egui_dialog_t *self);
};

Dialog 结构体

struct egui_dialog
{
    uint8_t state;                      // 当前状态
    uint8_t is_need_finish;             // 是否需要关闭
    uint8_t is_cancel_on_touch_outside; // 点击外部是否关闭

    egui_core_t *core;                  // Dialog 所属的 core

    egui_view_root_group_t root_view;      // 遮罩层(全屏)
    egui_view_root_group_t user_root_view; // 内容区域

    egui_activity_t *bind_activity;     // 绑定的 Activity

    const egui_dialog_api_t *api;       // 虚函数表
};

核心 API

// 启动 Dialog(绑定到指定 Activity)
void egui_dialog_start(egui_dialog_t *self, egui_activity_t *activity);

// 关闭 Dialog
void egui_dialog_finish(egui_dialog_t *self);

// 检查 Dialog 是否正处于切换流程中
int egui_dialog_check_in_process(egui_dialog_t *self);

// 通过视图反查当前 Dialog / Activity
egui_dialog_t *egui_view_get_dialog(egui_view_t *self);
egui_activity_t *egui_view_get_activity(egui_view_t *self);

// 设置 Dialog 的打开/关闭动画
void egui_dialog_set_anim(
    egui_dialog_t *self,
    egui_animation_t *open_anim,
    egui_animation_t *close_anim
);

// 设置 Dialog 内容区域的布局
void egui_dialog_set_layout(egui_dialog_t *self, egui_region_t *layout);

// 向 Dialog 内容区域添加子视图
void egui_dialog_add_view(egui_dialog_t *self, egui_view_t *view);

Dialog 动画配置

典型的底部上滑动画:

// 打开动画:从屏幕底部滑入
EGUI_ANIMATION_TRANSLATE_PARAMS_INIT(anim_dialog_start_param,
    0, 0, EGUI_CONFIG_SCREEN_HEIGHT, 0);
egui_animation_translate_t anim_dialog_start;

// 关闭动画:向屏幕底部滑出
EGUI_ANIMATION_TRANSLATE_PARAMS_INIT(anim_dialog_finish_param,
    0, 0, 0, -EGUI_CONFIG_SCREEN_HEIGHT);
egui_animation_translate_t anim_dialog_finish;

// 初始化并注册
egui_animation_translate_init(EGUI_ANIM_OF(&anim_dialog_start));
egui_animation_translate_params_set(&anim_dialog_start, &anim_dialog_start_param);
egui_animation_duration_set(EGUI_ANIM_OF(&anim_dialog_start), 500);

egui_animation_translate_init(EGUI_ANIM_OF(&anim_dialog_finish));
egui_animation_translate_params_set(&anim_dialog_finish, &anim_dialog_finish_param);
egui_animation_duration_set(EGUI_ANIM_OF(&anim_dialog_finish), 500);
egui_animation_is_fill_before_set(EGUI_ANIM_OF(&anim_dialog_finish), true);

egui_dialog_test_init((egui_dialog_t *)&dialog, core);
egui_dialog_set_anim(
    (egui_dialog_t *)&dialog,
    EGUI_ANIM_OF(&anim_dialog_start),
    EGUI_ANIM_OF(&anim_dialog_finish)
);

自定义 Dialog

以 HelloActivity 中的 egui_dialog_test_t 为例:

// 1. 定义结构体
struct egui_dialog_test
{
    egui_dialog_t base;

    egui_view_linearlayout_t layout_1;
    egui_view_label_t label_1;
    egui_view_button_t button_1;    // "Close" 按钮
};

// 2. 重写 on_create
void egui_dialog_test_on_create(egui_dialog_t *self)
{
    egui_dialog_test_t *local = (egui_dialog_test_t *)self;
    egui_dialog_on_create(self);    // 调用父类

    // 初始化控件
    egui_view_linearlayout_init((egui_view_t *)&local->layout_1, egui_dialog_get_core(self));
    // ... 设置按钮回调 ...

    // 添加到 Dialog 内容区域
    egui_dialog_add_view(self, (egui_view_t *)&local->layout_1);

    // 设置背景(圆角矩形)和布局位置(居中)
    egui_view_set_background(
        (egui_view_t *)&self->user_root_view,
        (egui_background_t *)&bg_dialog
    );
    egui_region_t region;
    egui_region_init(&region,
        (EGUI_CONFIG_SCREEN_WIDTH - DIALOG_WIDTH) / 2,
        (EGUI_CONFIG_SCREEN_HEIGHT - DIALOG_HEIGHT) / 2,
        DIALOG_WIDTH, DIALOG_HEIGHT);
    egui_dialog_set_layout(self, &region);
}

// 3. 关闭按钮回调
static void button_1_click_cb(egui_view_t *self)
{
    egui_dialog_t *p_dialog = egui_view_get_dialog(self);
    if (p_dialog)
    {
        egui_dialog_finish(p_dialog);
    }
}

使用流程

// 在 Activity 的按钮回调中启动 Dialog
static void open_dialog_cb(egui_view_t *self)
{
    egui_activity_t *p_activity = egui_view_get_activity(self);
    if (p_activity)
    {
        egui_dialog_start((egui_dialog_t *)&dialog, p_activity);
    }
}

相关文件

  • src/app/egui_dialog.h - Dialog 基类定义

  • src/app/egui_dialog.c - Dialog 基类实现

  • example/HelloActivity/egui_dialog_test.h/c - Dialog 示例实现

Toast Helper

Dialog 同样提供了基于对象的 Toast 快捷接口:

void egui_dialog_show_toast_info(egui_dialog_t *self, const char *text);
void egui_dialog_show_toast_info_with_duration(egui_dialog_t *self, const char *text, uint16_t duration);

典型用法:

static void button_1_click_cb(egui_view_t *self)
{
    egui_dialog_t *dialog = egui_view_get_dialog(self);

    if (dialog != NULL)
    {
        egui_dialog_show_toast_info(dialog, "Dialog closing");
        egui_dialog_finish(dialog);
    }
}

Core 解析说明

当前实现里,egui_dialog_start(self, activity) 只使用 dialog 自身已经绑定的 core。 如果 dialogdialog.root_viewdialog.user_root_view 与目标 activity 不属于同一个 core,启动流程会直接返回,不会做自动补绑,也不依赖 active core。

egui_dialog_show_toast_info(...) / egui_dialog_show_toast_info_with_duration(...) 这类对象式 helper, 则会通过 dialog 自身绑定的 core 去查找默认 Toast。 因此只要 Dialog 在初始化时已经带上目标屏幕的 core, 后续显示 Toast 时就不需要再额外传 core

多屏场景下更应该在构造 Dialog 时就明确目标屏幕,而不是把跨屏归属判断留到启动阶段。