跳到主要内容

第一个程序

恭喜!如果你的环境已经配置好,接下来我们写第一个真正的 HiEasyX 程序。不用担心,代码很短,而且我们会逐行讲解每一部分在做什么。

完整代码:Hello World

#include <include/hex.h>
#include <include/impl/EasyX/hex_impl_easyx.h>
#include <graphics.h> // EasyX 图形库
#include <string>

int main() {
// ========== 1. 初始化图形窗口 ==========
initgraph(800, 600);

// ========== 2. 初始化 HiEasyX ==========
HX::HXInitForEasyX();
HX::SetBuffer(GetWorkingImage());
HX::ApplyModernTheme(HX::HXThemeMode::ModernDark);

// ========== 3. 主循环 ==========
bool running = true;
while (running) {
// ---- 3.1 帧开始 ----
HX::HXBegin();

// ---- 3.2 处理消息 ----
ExMessage msg;
while (peekmessage(&msg)) {
HX::PushMessage(HX::GetHXMessage(&msg));
}

// ---- 3.3 创建窗口并放置控件 ----
HX::WindowProfile wp;
wp.Size = {400, 300};
if (HX::Window(HXStr("Hello World"), wp)) {
HX::Text(HXStr("欢迎使用 HiEasyX!"));
HX::Text(HXStr("这是一个简单的示例窗口。"));

HX::ButtonProfile bp;
if (HX::Button(HXStr("点击我"), bp)) {
// 按钮被点击时进入这里
}
}

// ---- 3.4 帧结束 ----
HX::End();

// ---- 3.5 渲染到屏幕 ----
HX::Render();

// ---- 3.6 退出检测 ----
if (msg.message == WM_CLOSE) {
running = false;
}
}

// ========== 4. 清理 ==========
closegraph();
return 0;
}

逐行讲解

初始化阶段

initgraph(800, 600);

这是 EasyX 的函数,创建一个 800×600 的图形窗口。HiEasyX 不会替你创建窗口,这是它和重型框架(如 Qt)的一个区别——底层窗口仍然由你控制

HX::HXInitForEasyX();

告诉 HiEasyX:"我要和 EasyX 一起工作"。这会初始化内部的运行时上下文和主题系统。

HX::SetBuffer(GetWorkingImage());

将 EasyX 当前的工作图像(也就是窗口的后台缓冲区)设为 HiEasyX 的绘制目标。你可以理解为:HiEasyX 画的所有东西,最终都会落到这张"画布"上。

HX::ApplyModernTheme(HX::HXThemeMode::ModernDark);

应用现代暗色主题。HiEasyX 默认有两套主题:

主题模式风格
ModernDark高对比度暗色,适合大多数场景
ModernDim低对比度暗色,长时间工作更护眼

主循环阶段

HiEasyX 采用 IMGUI(Immediate Mode GUI) 模式,也就是说:界面不是"创建一次就放着不管",而是每帧都重新描述一遍

HX::HXBegin();

帧开始。HiEasyX 会重置这一帧的临时状态(比如消息查询、布局栈),但会保留需要持久化的数据(比如输入框里的文字、窗口位置)。

ExMessage msg;
while (peekmessage(&msg)) {
HX::PushMessage(HX::GetHXMessage(&msg));
}

这是消息处理的标准写法。peekmessage 从 EasyX 获取鼠标、键盘事件,GetHXMessage 把它转成 HiEasyX 内部格式,再用 PushMessage 送进去。

💡 为什么要手动推送消息?

因为 HiEasyX 是一个无状态 IMGUI 层,它不会自己挂窗口消息钩子。这样做的好处是:你可以完全控制消息流,甚至在推送前过滤、修改消息。

HX::WindowProfile wp;
wp.Size = {400, 300};
if (HX::Window(HXStr("Hello World"), wp)) {
// ... 控件 ...
}

创建一个窗口面板。Window() 返回 bool,只有当前窗口处于激活状态时才返回 true**。所有放在这个 if 块里的控件,都会绘制到这个窗口内部。

ℹ️ HXStr 是什么?

HiEasyX 的字符串类型 HXString 实际上是 std::wstringHXStr("Hello World") 是一个宏,在 Unicode 模式下展开为 L"Hello World"。使用宏可以让代码更简洁,也避免了忘记加 L 前缀导致的编译错误。

HX::End();

帧结束。HiEasyX 在这里做一些收尾工作,比如处理 Tab 键导航、解锁 EasyX 的绘图状态等。

HX::Render();

真正绘制到屏幕。在此之前,所有的绘制操作都是记录在内部的命令缓冲或子缓冲区中;Render() 把它们合成到最终的图像上,然后显示出来。

⚠️ 不要忘记 Render()!

如果你发现窗口是一片黑,或者控件不显示,第一件事就是检查主循环里有没有调用 HX::Render()。这是新手最容易踩的坑之一。

运行效果预期

运行上面的程序后,你应该会看到一个:

  • 800×600 的图形窗口(由 initgraph 创建)
  • 窗口中央有一个 400×300 的深色面板(HiEasyX 的 Window
  • 面板顶部有标题栏 "Hello World"
  • 面板内有两行文字和一个"点击我"按钮
  • 按钮在鼠标悬停时会有高亮反馈

常见错误排查

现象原因解决
窗口全黑,什么都没有忘了调用 HX::Render()在循环末尾加上 HX::Render();
控件不响应鼠标消息没推进去确认 peekmessage + PushMessage 的代码在 HXBegin() 之后
按钮点击没反应Button() 返回值没判断Button() 放到 if 条件里:if (HX::Button(...)) { ... }
编译报错 HXStr 未定义没开 Unicode项目属性 → 高级 → 字符集 → 使用 Unicode
文字显示为方块或乱码字体或编码问题确保使用了 HXStr(),且系统支持默认字体
💡 进阶小练习

试着修改代码,把窗口改成可移动的:

wp.Moveable = true;

然后再加一个新窗口,看看两个窗口能不能叠在一起。HiEasyX 的窗口系统支持完整的层级和聚焦管理。

→ 继续阅读:核心概念