/** * Window(头文件) * * 设计目标: * - 提供一个基于 Win32 + EasyX 的“可拉伸且稳定不抖”的窗口容器。 * - 通过消息过程子类化(WndProcThunk)接管关键消息(WM_SIZING/WM_SIZE/...)。 * - 将“几何变化记录(pendingW/H)”与“统一收口重绘(needResizeDirty)”解耦。 * * 关键点(与 .cpp 中实现对应): * - isSizing:处于交互拉伸阶段时,冻结重绘;松手后统一重绘,防止抖动。 * - WM_SIZING:只做“最小尺寸夹紧”,不回滚矩形、不做对齐;把其余交给系统。 * - WM_GETMINMAXINFO:按最小“客户区”换算到“窗口矩形”,提供系统层最小轨迹值。 * - runEventLoop:只记录 WM_SIZE 的新尺寸;真正绘制放在 needResizeDirty 时集中处理。 */ #pragma once #include "Control.h" #include #include #include #include class Window { // —— 尺寸状态 ——(绘制尺寸与待应用尺寸分离;收口时一次性更新) int width; // 当前有效宽(已应用到画布/控件的客户区宽) int height; // 当前有效高(已应用到画布/控件的客户区高) int pendingW; // 待应用宽(WM_SIZE/拉伸中记录用) int pendingH; // 待应用高 int minClientW; // 业务设定的最小客户区宽(用于 GETMINMAXINFO 与 SIZING 夹紧) int minClientH; // 业务设定的最小客户区高 int windowMode = NULL; // EasyX 初始化模式(EX_SHOWCONSOLE/EX_TOPMOST/...) bool needResizeDirty = false; // 统一收口重绘标志(置位后在事件环末尾处理) bool isSizing = false; // 是否处于拖拽阶段(ENTER/EXIT SIZEMOVE 切换) // —— 原生窗口句柄与子类化钩子 ——(子类化 EasyX 的窗口过程以拦截关键消息) HWND hWnd = NULL; // EasyX 初始化后的窗口句柄 WNDPROC oldWndProc = nullptr; // 保存旧过程(CallWindowProc 回落) bool procHooked = false; // 避免重复子类化 static LRESULT CALLBACK WndProcThunk(HWND h, UINT m, WPARAM w, LPARAM l); // 静态过程分发到 this // —— 绘制相关 ——(是否使用合成双缓冲、窗口标题、背景等) bool useComposited = true; // 是否启用 WS_EX_COMPOSITED(部分机器可能增加一帧观感延迟) std::string headline; // 窗口标题文本 COLORREF wBkcolor = BLACK; // 纯色背景(无背景图时使用) IMAGE* background = nullptr; // 背景图对象指针(存在时优先绘制) std::string bkImageFile; // 背景图文件路径(loadimage 用) // —— 控件/对话框 ——(容器内的普通控件与非模态对话框) std::vector> controls; std::vector> dialogs; public: bool dialogClose = false; // 项目内使用的状态位 // —— 构造/析构 ——(仅初始化成员;实际样式与子类化在 draw() 中完成) Window(int width, int height, int mode); Window(int width, int height, int mode, COLORREF bkcloc); Window(int width, int height, int mode, COLORREF bkcloc, std::string headline); ~Window(); // —— 绘制与事件循环 ——(draw* 完成一次全量绘制;runEventLoop 驱动事件与统一收口) void draw(); // 纯色背景版本 void draw(std::string pImgFile); // 背景图版本 int runEventLoop(); // 主事件循环(PeekMessage + 统一收口重绘) // —— 背景/标题设置 ——(更换背景、背景色与标题;立即触发一次批量绘制) void setBkImage(std::string pImgFile); void setBkcolor(COLORREF c); void setHeadline(std::string headline); // —— 控件/对话框管理 ——(添加到容器,或做存在性判断) void addControl(std::unique_ptr control); void addDialog(std::unique_ptr dialogs); bool hasNonModalDialogWithCaption(const std::string& caption, const std::string& message) const; // —— 访问器 ——(只读接口,供外部查询当前窗口/标题/背景等) HWND getHwnd() const; int getWidth() const; int getHeight() const; std::string getHeadline() const; COLORREF getBkcolor() const; IMAGE* getBkImage() const; std::string getBkImageFile() const; std::vector>& getControls(); // —— 配置开关 ——(动态调整最小客户区、合成双缓冲) inline void setMinClientSize(int w, int h) { // 仅更新阈值;实际约束在 WM_GETMINMAXINFO/WM_SIZING 中生效 minClientW = w; minClientH = h; } inline void setComposited(bool on) { // 更新标志;真正应用在 draw()/样式 SetWindowLongEx + SWP_FRAMECHANGED useComposited = on; } void processWindowMessage(const ExMessage & msg); // 处理 EX_WINDOW 中的 WM_SIZE 等 void pumpResizeIfNeeded(); // 执行一次统一收口重绘 void scheduleResizeFromModal(int w, int h); };