窗口 Window
窗口是 HiEasyX 里一切控件的"家"。没有窗口,按钮、文本、滑块都无处安放。你可以把它理解为一块带有标题栏的浮动面板,支持拖拽移动、缩放大小,甚至还能折叠起来。
HiEasyX 采用 IMGUI(即时模式 GUI) 架构,每一帧我们都会重新描述整个界面。所以 Window() 函数每帧都要调用,框架会自动帮你管理窗口的状态。
在 HiEasyX 中,Window() 创建的并不是操作系统级别的真实窗口,而是绘制在 EasyX 画布上的虚拟窗口。这意味着你可以同时创建很多个窗口,它们都渲染在同一张画布上,互相重叠、拖拽、缩放,完全由框架接管。
函数原型
void Window(const HXString &Title, WindowProfile &Profile);
参数说明
| 参数 | 类型 | 说明 |
|---|---|---|
Title | const HXString & | 窗口标题,会显示在标题栏上。使用 HXStr("标题") 或 L"标题"。 |
Profile | WindowProfile & | 窗口的配置结构体。必须是 static 或全局变量,不能是局部变量! |
WindowProfile 结构体
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
Size | HXPoint | {300, 200} | 窗口内容区域的宽高(不包含标题栏)。 |
TitleBarHeight | int | 40 | 标题栏的高度(像素)。 |
TitleFontSize | int | 20 | 标题栏文字的字体大小。 |
MinSize | HXPoint | {-1, -1} | 最小尺寸限制。{-1, -1} 表示不限制。 |
MaxSize | HXPoint | {-1, -1} | 最大尺寸限制。{-1, -1} 表示不限制。 |
Position | HXPoint | {0, 0} | 窗口左上角的坐标。 |
Sizable | bool | true | 是否允许通过拖拽右下角调整窗口大小。 |
Movable | bool | true | 是否允许通过拖拽标题栏移动窗口。 |
Folded | bool | false | 窗口是否处于折叠状态。由框架自动维护,你通常只需读取它。 |
InDrag | bool | false | 当前是否正在拖拽窗口。由框架自动维护。 |
DeltaX | int | 0 | 拖拽过程中鼠标在 X 方向的偏移量。内部使用。 |
DeltaY | int | 0 | 拖拽过程中鼠标在 Y 方向的偏移量。内部使用。 |
Size 指的是内容区域的大小,也就是标题栏下方用来放控件的那块地方。窗口实际占用的总高度是 TitleBarHeight + Size.y。
WindowProfile 必须是 static 或全局变量。如果你在函数内部写成局部变量,每一帧都会丢失上一次的窗口位置、大小、折叠状态,窗口会变得无法拖动!
返回值
Window() 没有返回值。但如果你想要知道窗口是否被折叠了,直接读取 Profile.Folded 即可。
多窗口管理
HiEasyX 天然支持多窗口。你只需要连续调用多次 Window(),并给每个窗口一个唯一的标题,框架就会自动帮你管理它们:
static HX::WindowProfile wp1;
wp1.Size = {300, 200};
wp1.Position = {50, 50};
HX::Window(HXStr("工具箱"), wp1);
// ... 在工具箱里放控件 ...
static HX::WindowProfile wp2;
wp2.Size = {400, 300};
wp2.Position = {400, 100};
HX::Window(HXStr("属性面板"), wp2);
// ... 在属性面板里放控件 ...
窗口的绘制顺序就是你在代码里的调用顺序,后调用的窗口会覆盖在前面的窗口之上。
拖拽与缩放
只要 Movable = true,用户就可以用鼠标拖动标题栏来移动窗口;只要 Sizable = true,就可以拖动右下角的小三角来改变窗口大小。
如果你想禁止某个窗口被拖动(比如固定在工作区边缘的工具栏),这样写:
static HX::WindowProfile wp;
wp.Movable = false;
wp.Sizable = false;
HX::Window(HXStr("固定面板"), wp);
折叠窗口
用户点击标题栏右侧的折叠按钮,窗口就会在"展开"和"折叠"之间切换。折叠后只保留标题栏,Folded 字段会被自动设为 true。
你也可以在代码里手动控制折叠:
static HX::WindowProfile wp;
if (HX::Button(HXStr("切换折叠"), btnProfile)) {
wp.Folded = !wp.Folded;
}
HX::Window(HXStr("可折叠窗口"), wp);
保存与恢复位置
如果你想让窗口的位置和大小在程序重启后依然保持,可以配合 DockSpace 的序列化功能来实现:
// 程序启动时加载
HX::LoadDockLayout(HXStr("layout.ini"));
// 程序退出前保存
HX::SaveDockLayout(HXStr("layout.ini"));
这两个函数来自 hex_dockspace.h,不仅保存 DockSpace 的分割比例,也会保存所有窗口的位置和尺寸。如果窗口使用了 DockSpace 中的 BeginDockSlot,布局恢复会更加智能。对于独立的浮动窗口,框架会匹配 [Window:Title] 段落来还原位置和大小。
完整示例代码
下面是一个完整的、可直接编译运行的示例。我们创建了两个窗口,一个可以拖拽缩放,另一个固定不动;还有一个按钮可以切换第一个窗口的折叠状态。
#include <include/hex.h>
#include <include/impl/EasyX/hex_impl_easyx.h>
int main() {
// 初始化 EasyX
initgraph(1024, 768);
setbkcolor(WHITE);
cleardevice();
HX::HXInitForEasyX();
HX::SetBuffer(GetWorkingImage());
BeginBatchDraw();
// ===== 必须是 static 或全局变量 =====
static HX::WindowProfile wp1;
wp1.Size = {350, 250};
wp1.Position = {100, 100};
wp1.TitleBarHeight = 40;
wp1.TitleFontSize = 20;
wp1.Sizable = true;
wp1.Movable = true;
static HX::WindowProfile wp2;
wp2.Size = {250, 200};
wp2.Position = {600, 150};
wp2.Movable = false; // 禁止拖动
wp2.Sizable = false; // 禁止缩放
static HX::ButtonProfile btnProfile;
while (true) {
HX::HXBegin();
ExMessage msg;
while (peekmessage(&msg)) {
HX::PushMessage(HX::GetHXMessage(&msg));
}
// ---------- 第一个窗口:主面板 ----------
HX::Window(HXStr("主面板"), wp1);
HX::Text(HXStr("这是一个可以拖拽、缩放的窗口"));
HX::Text(HXStr("试试拖动标题栏,或拉右下角"));
if (HX::Button(HXStr("切换折叠"), btnProfile)) {
wp1.Folded = !wp1.Folded;
}
// ---------- 第二个窗口:固定信息栏 ----------
HX::Window(HXStr("信息栏"), wp2);
HX::Text(HXStr("这个窗口是固定的"));
HX::Text(HXStr("无法拖动或缩放"));
HX::End();
HX::Render();
FlushBatchDraw();
Sleep(16);
Sleep(16); // 约 60 FPS
}
closegraph();
return 0;
}
刚开始写 HiEasyX 的时候,很容易把 WindowProfile 写成局部变量,然后发现窗口拖不动。记住这个口诀:Profile 用 static,状态才能扛。
运行效果
截图占位符:请补充 $name 控件的运行效果截图。
截图占位符:请补充
Window 运行效果的运行效果截图,保存为./assets/Window_view.png。`n