跳到主要内容

窗口 Window

窗口是 HiEasyX 里一切控件的"家"。没有窗口,按钮、文本、滑块都无处安放。你可以把它理解为一块带有标题栏的浮动面板,支持拖拽移动、缩放大小,甚至还能折叠起来。

HiEasyX 采用 IMGUI(即时模式 GUI) 架构,每一帧我们都会重新描述整个界面。所以 Window() 函数每帧都要调用,框架会自动帮你管理窗口的状态。

先理解这个

在 HiEasyX 中,Window() 创建的并不是操作系统级别的真实窗口,而是绘制在 EasyX 画布上的虚拟窗口。这意味着你可以同时创建很多个窗口,它们都渲染在同一张画布上,互相重叠、拖拽、缩放,完全由框架接管。


函数原型

void Window(const HXString &Title, WindowProfile &Profile);

参数说明

参数类型说明
Titleconst HXString &窗口标题,会显示在标题栏上。使用 HXStr("标题")L"标题"
ProfileWindowProfile &窗口的配置结构体。必须是 static 或全局变量,不能是局部变量!

WindowProfile 结构体

字段类型默认值说明
SizeHXPoint{300, 200}窗口内容区域的宽高(不包含标题栏)。
TitleBarHeightint40标题栏的高度(像素)。
TitleFontSizeint20标题栏文字的字体大小。
MinSizeHXPoint{-1, -1}最小尺寸限制。{-1, -1} 表示不限制。
MaxSizeHXPoint{-1, -1}最大尺寸限制。{-1, -1} 表示不限制。
PositionHXPoint{0, 0}窗口左上角的坐标。
Sizablebooltrue是否允许通过拖拽右下角调整窗口大小。
Movablebooltrue是否允许通过拖拽标题栏移动窗口。
Foldedboolfalse窗口是否处于折叠状态。由框架自动维护,你通常只需读取它。
InDragboolfalse当前是否正在拖拽窗口。由框架自动维护
DeltaXint0拖拽过程中鼠标在 X 方向的偏移量。内部使用
DeltaYint0拖拽过程中鼠标在 Y 方向的偏移量。内部使用
关于 Size 的理解

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"));
关于 SaveDockLayout / LoadDockLayout

这两个函数来自 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