EUI-NEO

事件系统

EUI-NEO 技术文档。

事件系统

事件系统位于 core/event.h,由 core::dsl::Runtime 在每次 update 时统一驱动。页面和组件不直接读 GLFW 鼠标状态,也不自己维护 hover / pressed。

PointerEvent

struct PointerEvent {
    double x;
    double y;
    double deltaX;
    double deltaY;
    bool down;
    bool pressedThisFrame;
    bool releasedThisFrame;
};

当前封装了鼠标左键 pointer、文本输入、滚轮和拖拽事件。右键、多指针、IME 组合态显示还没有下沉。

InteractionState

struct InteractionState {
    bool hover;
    bool pressed;
    bool clicked;
    bool pressStarted;
    bool released;
    bool drag;
    bool active;
    bool changed;
    double dragStartX;
    double dragStartY;
    double dragDeltaX;
    double dragDeltaY;
};

按下捕获是底层交互系统的一部分:在某个元素上按下后,拖动期间不会让其它元素抢走这次按下序列。拖出再释放不会触发 click,拖出后再拖回元素内释放会触发 click。

DSL 写法

通用 builder 现在支持交互:

ui.text("link")
    .size(240.0f, 32.0f)
    .text("Open project")
    .interactive()
    .onClick([] {
        core::platform::openUrl("https://github.com/sudoevolve/EUI-NEO");
    })
    .build();

Row / Column / Stack / Rect / Text / Image 都可以通过 .interactive().onClick(...) 进入命中测试。.onClick(...) 会自动开启 interactive,并把 cursor 设置为手型。

通用交互方法:

.interactive(true)
.disabled(false)
.enabled(true)
.cursor(core::CursorShape::Hand)
.onClick(callback)
.focusable()
.onFocusChanged(callback)
.onTextInput(callback)
.onScroll(callback)
.onDrag(callback)

Rect 状态

只有 Rect 目前内置 hover / pressed 视觉状态:

ui.rect("button.bg")
    .size(240.0f, 70.0f)
    .states(normal, hover, pressed)
    .transition(0.16f, core::Ease::OutCubic)
    .onClick(callback)
    .build();

.states(normal, hover, pressed) 会设置:

Runtime 使用 SmoothedValue<float> 维护 hover / press blend,再混合颜色:

normal -> hover -> pressed

组件中的事件

组件不要自己读鼠标,也不要保存 InteractionState。组件只声明 DSL 树,把点击回调交给 Runtime:

ui.stack(id)
    .size(w, h)
    .visualStateFrom(id + ".bg", 0.965f)
    .content([&] {
        ui.rect(id + ".bg")
            .size(w, h)
            .states(normal, hover, pressed)
            .onClick(onClick_)
            .build();

        ui.text(id + ".label")
            .size(w, h)
            .text(text_)
            .build();
    })
    .build();

components::button(ui, id) 就是这种写法:内部背景 rect 负责状态和点击,外层 stack 用 visualStateFrom 跟随按压缩放。

Runtime 流程

每次 update 大致流程:

1. readPointerEvent(window, pointerScale) 读取 pointer。 2. consumeInputEvents() 消费 GLFW char / key / scroll callback 收集到的输入。 3. 鼠标按下时更新 focused element。 4. 从 DSL 树中找出最上层命中的 interactive 元素;如果已有 active 捕获元素,则优先使用捕获元素。 5. 更新每个 interactive 元素的 InteractionState。 6. 派发 onClick / onDrag / onScroll / onTextInput,并在需要时设置 needsCompose()。 7. 根据 hover 设置 GLFW 手型 cursor。 8. Rect 根据交互状态推进 hover / press 缓动并标记 dirty rect。

外部链接

跨平台打开 URL 使用 core/platform.h

#include "core/platform.h"

core::platform::openUrl("https://github.com/sudoevolve/EUI-NEO");

当前实现:

openUrl 返回 bool 表示是否成功发起打开动作。它不等待浏览器或外部程序结束。

与脏区渲染

交互状态变化不会默认整页重绘:

当前限制