EUI-NEO

窗口与页面

EUI-NEO 技术文档。

窗口与页面

当前项目把窗口主循环和页面声明分开:

页面开发通常只需要写 app/*.cpp

DSL App 入口

使用 DSL 的 app 文件包含:

#include "app/dsl_app.h"

然后实现:

namespace app {

const DslAppConfig& dslAppConfig();
void compose(core::dsl::Ui& ui, const core::dsl::Screen& screen);

} // namespace app

DslAppConfig

当前字段:

struct DslAppConfig {
    const char* title = "App";
    const char* pageId = "app";
    core::Color clearColor = {0.16f, 0.18f, 0.20f, 1.0f};
    int windowWidth = 800;
    int windowHeight = 600;
    bool showFrameCountInTitle = false;
    double fps = 0.0;
    const char* iconPath = "assets/icon.png";
};

示例:

const DslAppConfig& dslAppConfig() {
    static const DslAppConfig config = {
        "Calculator",
        "calculator",
        {0.07f, 0.075f, 0.085f, 1.0f},
        430,
        760,
        false,
        90.0
    };
    return config;
}

calculator 当前把 fps 写成 90.0gallery 默认 fps = 90.0;Settings 中打开 Unlock 90 FPS limit 后会把 fps 同步为 0.0,回到显示器刷新率。默认关闭 Glass/blur。

compose

compose(core::dsl::Ui& ui, const core::dsl::Screen& screen) 是页面声明入口。

screen.widthscreen.height 是逻辑尺寸,已经由 app/dsl_app.h 根据 DPI scale 换算。页面布局应使用这个逻辑尺寸,不要直接取 framebuffer 像素尺寸。

推荐根节点写法:

void compose(core::dsl::Ui& ui, const core::dsl::Screen& screen) {
    ui.row("root")
        .size(screen.width, screen.height)
        .content([&] {
            // sidebar
            // content
        })
        .build();
}

页面状态可以放在 app/*.cpp 的匿名 namespace 中:

namespace {

int selectedPage = 0;
bool optionMotion = true;

} // namespace

点击事件修改状态后,app/dsl_app.h 会在 Runtime 需要重新 compose 时重新声明页面,并标记 full redraw。

主循环行为

main.cpp 当前负责:

当前主动节流使用 glfwSwapInterval(0),避免和 SwapBuffers 的 vsync 阻塞叠加。

Windows 下会调用 timeBeginPeriod(1),并在 CMake 中链接 winmm,用于改善高刷新率等待精度。

dsl_app.h 行为

app/dsl_app.h 内部持有一个静态 core::dsl::Runtime

它不会每轮都 compose。当前只在这些情况 compose:

点击导致重新 compose 后,会立即用 deltaSeconds = 0 调一次 Runtime update,让新 DSL 树和 Runtime 缓存实例同步,然后标记 full redraw。

Demo 程序

当前 app/ 下主要有:

CMakeLists.txt 会扫描 app/*.cpp

file(GLOB APP_SOURCES CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/app/*.cpp")
foreach(app_source ${APP_SOURCES})
    get_filename_component(app_name "${app_source}" NAME_WE)
    add_executable(${app_name} main.cpp "${app_source}" ${CORE_SOURCES})
    configure_opengl_app(${app_name})
endforeach()

所以新增 app/example.cpp 后,会生成 example.exe

脏区与整屏重绘

当前 DSL Runtime 支持 framebuffer cache + scissor 的脏区渲染。

常见局部重绘:

常见 full redraw:

页面和组件不用自己实现脏区,但要做到:

当前限制